### Setup Medusa backend and storefront (bash)
Source: https://github.com/rpuls/medusa-b2b-for-railway/blob/main/README.md
Provides commands to clone the Medusa B2B starter repository, configure the backend environment, install dependencies, set up the database, seed data, create an admin user, and start the backend server. Requires Git, Yarn, and a compatible database. Run in a terminal on your development machine.
```bash
# Clone the repository
git clone https://github.com/medusajs/b2b-starter-medusa.git
## Setup Backend
# Go to the folder
cd ./backend
# Clone .env.template
cp .env.template .env
# Install dependencies
yarn install
# Install dependencies, setup database & seed data
yarn install && yarn medusa db:create && yarn medusa db:migrate && yarn run seed yarn medusa user -e admin@test.com -p supersecret -i admin
# Start Medusa project - backend & admin
yarn dev
## Setup Storefront
# Go to folder
cd ../storefront
# Clone .env.template
cp .env.template .env
# Install dependencies
yarn install
```
--------------------------------
### Advanced Product Created Subscriber with Service Interaction (TypeScript)
Source: https://github.com/rpuls/medusa-b2b-for-railway/blob/main/backend/src/subscribers/README.md
A more advanced TypeScript subscriber example that retrieves product details using the Medusa container. It demonstrates accessing services and handling event data to log the product title.
```typescript
import type { SubscriberArgs, SubscriberConfig } from "@medusajs/medusa";
import { IProductModuleService } from "@medusajs/framework/types";
import { ModuleRegistrationName } from "@medusajs/framework/utils";
export default async function productCreateHandler({
event: { data },
container,
}: SubscriberArgs<{ id: string }>) {
const productId = data.id;
const productModuleService: IProductModuleService = container.resolve(
ModuleRegistrationName.PRODUCT
);
const product = await productModuleService.retrieve(productId);
console.log(`The product ${product.title} was created`);
}
export const config: SubscriberConfig = {
event: "product.created",
};
```
--------------------------------
### Medusa Admin Company List Page Example (TypeScript)
Source: https://context7.com/rpuls/medusa-b2b-for-railway/llms.txt
Demonstrates the usage of `useCompanies` and `useCreateCompany` hooks within a React component for the Medusa Admin UI. This example shows how to fetch a list of companies, display them, and handle the creation of a new company via a form submission.
```typescript
// Usage in Admin UI Component
import { useCompanies, useCreateCompany } from "@/hooks/api/companies";
import { Button, Input, Container } from "@medusajs/ui";
function CompaniesListPage() {
const { companies, isLoading } = useCompanies();
const createCompany = useCreateCompany();
const handleCreate = async (formData: FormData) => {
await createCompany.mutateAsync({
name: formData.get("name") as string,
email: formData.get("email") as string,
spending_limit_reset_frequency: "monthly"
});
};
if (isLoading) return
Loading...
;
return (
{companies?.map(company => (
{company.name} - {company.employees?.length} employees
))}
);
}
```
--------------------------------
### Basic Product Created Subscriber (TypeScript)
Source: https://github.com/rpuls/medusa-b2b-for-railway/blob/main/backend/src/subscribers/README.md
An example of a basic subscriber in TypeScript that logs a message when a product is created. It defines the subscriber function and its configuration, listening for the 'product.created' event.
```typescript
import { type SubscriberConfig } from "@medusajs/medusa";
// subscriber function
export default async function productCreateHandler() {
console.log("A product was created");
}
// subscriber config
export const config: SubscriberConfig = {
event: "product.created",
};
```
--------------------------------
### Start Medusa storefront (bash)
Source: https://github.com/rpuls/medusa-b2b-for-railway/blob/main/README.md
Runs the storefront development server using Yarn. Ensure the backend is running and the .env file contains the publishable key. Execute in the storefront directory.
```bash
# Start Medusa storefront
yarn dev
```
--------------------------------
### GET /store/hello-world
Source: https://github.com/rpuls/medusa-b2b-for-railway/blob/main/backend/src/api/README.md
A simple GET endpoint that returns a hello world message. This demonstrates the basic structure of a custom API route in Medusa.
```APIDOC
## GET /store/hello-world
### Description
A simple GET endpoint that returns a hello world message. This demonstrates the basic structure of a custom API route in Medusa.
### Method
GET
### Endpoint
/store/hello-world
### Parameters
No parameters required.
### Request Example
```bash
curl -X GET http://localhost:9000/store/hello-world
```
### Response
#### Success Response (200)
- **message** (string) - The hello world message
#### Response Example
```json
{
"message": "Hello world!"
}
```
```
--------------------------------
### Configure MinIO Environment Variables
Source: https://github.com/rpuls/medusa-b2b-for-railway/blob/main/backend/src/modules/minio-file/README.md
Defines required environment variables for MinIO connection including endpoint, credentials, and optional bucket name. These variables must be set before starting the Medusa server. The bucket parameter defaults to 'medusa-media' if not specified.
```env
MINIO_ENDPOINT=your-minio-endpoint
MINIO_ACCESS_KEY=your-access-key
MINIO_SECRET_KEY=your-secret-key
MINIO_BUCKET=your-bucket-name # Optional, defaults to 'medusa-media'
```
--------------------------------
### Create Basic GET API Route in Medusa
Source: https://github.com/rpuls/medusa-b2b-for-railway/blob/main/backend/src/api/README.md
Defines a simple GET endpoint at `/store/hello-world` that returns a JSON response. Routes are created as TypeScript files in the `/src/api` directory with the filename `route.ts`. The handler function receives MedusaRequest and MedusaResponse objects for request handling and response construction.
```ts
import type { MedusaRequest, MedusaResponse } from "@medusajs/medusa";
export async function GET(req: MedusaRequest, res: MedusaResponse) {
res.json({
message: "Hello world!",
});
}
```
--------------------------------
### Manage Company Employees using Medusa Store and Admin APIs
Source: https://context7.com/rpuls/medusa-b2b-for-railway/llms.txt
Provides examples for managing employees within a company, including creating, updating, and deleting employees, as well as listing them via the Admin API. Requires company admin or admin tokens.
```typescript
// Create employee (Company admin only)
const employeeResponse = await fetch(
'http://localhost:9000/store/companies/comp_123/employees',
{
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': 'Bearer {company_admin_token}'
},
body: JSON.stringify({
customer_id: "cus_789", // Existing customer to link
spending_limit: 5000, // $50.00 (amount in cents)
is_admin: false
})
}
);
const { employee } = await employeeResponse.json();
// Returns: { employee: { id: "emp_456", spending_limit: 5000, is_admin: false } }
// Update employee spending limit
await fetch('http://localhost:9000/store/companies/comp_123/employees/emp_456', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': 'Bearer {company_admin_token}'
},
body: JSON.stringify({
spending_limit: 10000, // Increase to $100.00
is_admin: true // Promote to admin
})
});
// Delete employee
await fetch('http://localhost:9000/store/companies/comp_123/employees/emp_456', {
method: 'DELETE',
headers: { 'Authorization': 'Bearer {company_admin_token}' }
});
// List company employees (Admin API)
const employeesResponse = await fetch(
'http://localhost:9000/admin/companies/comp_123/employees',
{
headers: { 'Authorization': 'Bearer {admin_token}' }
}
);
const { employees } = await employeesResponse.json();
```
--------------------------------
### GET /store/quotes/{quote_id}/preview
Source: https://context7.com/rpuls/medusa-b2b-for-railway/llms.txt
Preview the changes associated with a quote, showing updated prices and line items before acceptance.
```APIDOC
## GET /store/quotes/{quote_id}/preview
### Description
Preview quote changes before accepting.
### Method
GET
### Endpoint
/store/quotes/{quote_id}/preview
### Parameters
#### Path Parameters
- **quote_id** (string) - Required - The ID of the quote to preview.
### Request Example
```http
GET /store/quotes/quote_789/preview
```
### Response
#### Success Response (200)
- **order_preview** (object) - Contains a preview of the order changes, including updated prices and line items.
#### Response Example
```json
{
"order_preview": {
// ... order preview details ...
}
}
```
```
--------------------------------
### Handle Path Parameters in Medusa Routes
Source: https://github.com/rpuls/medusa-b2b-for-railway/blob/main/backend/src/api/README.md
Shows how to create routes with dynamic path parameters by using square bracket directory names. Parameters are accessed through `req.params`. This example creates a route that accepts a `productId` parameter and demonstrates how the directory structure maps to URL paths.
```ts
import type { MedusaRequest, MedusaResponse } from "@medusajs/medusa";
export async function GET(req: MedusaRequest, res: MedusaResponse) {
const { productId } = req.params;
res.json({
message: `You're looking for product ${productId}`,
});
}
```
--------------------------------
### GET /store/quotes
Source: https://context7.com/rpuls/medusa-b2b-for-railway/llms.txt
List customer quotes with pagination support. Allows filtering and field selection for detailed retrieval.
```APIDOC
## GET /store/quotes
### Description
List customer quotes with pagination.
### Method
GET
### Endpoint
/store/quotes
### Parameters
#### Query Parameters
- **limit** (integer) - Optional - The maximum number of quotes to return.
- **offset** (integer) - Optional - The number of quotes to skip before starting to collect.
- **fields** (string) - Optional - Comma-separated list of fields to include in the response (e.g., "*messages,*draft_order.items").
### Request Example
```http
GET /store/quotes?limit=10&offset=0&fields=*messages,*draft_order.items
```
### Response
#### Success Response (200)
- **quotes** (array) - An array of quote objects.
- **count** (integer) - The total number of quotes available.
- **offset** (integer) - The offset used for pagination.
- **limit** (integer) - The limit used for pagination.
#### Response Example
```json
{
"quotes": [
// ... quote objects ...
],
"count": 100,
"offset": 0,
"limit": 10
}
```
```
--------------------------------
### Create a Product Widget in React
Source: https://github.com/rpuls/medusa-b2b-for-railway/blob/main/backend/src/admin/README.md
This example demonstrates how to create a widget using React and inject it into a product details page in the Medusa Admin. The widget displays the text 'Product Widget' at the specified zone. The implementation involves defining a React component and configuring its placement.
```typescript
import { defineWidgetConfig } from "@medusajs/admin-sdk"
// The widget
const ProductWidget = () => {
return (
Product Widget
)
}
// The widget's configurations
export const config = defineWidgetConfig({
zone: "product.details.after",
})
export default ProductWidget
```
--------------------------------
### GET /api/products/{productId}
Source: https://github.com/rpuls/medusa-b2b-for-railway/blob/main/backend/src/api/README.md
An endpoint that accepts a path parameter for retrieving product information. Demonstrates how to access path parameters in route handlers.
```APIDOC
## GET /api/products/{productId}
### Description
An endpoint that accepts a path parameter for retrieving product information. Demonstrates how to access path parameters in route handlers.
### Method
GET
### Endpoint
/api/products/{productId}
### Parameters
#### Path Parameters
- **productId** (string) - Required - The unique identifier of the product
### Request Example
```bash
curl -X GET http://localhost:9000/api/products/prod_123
```
### Response
#### Success Response (200)
- **message** (string) - Message containing the product ID
#### Response Example
```json
{
"message": "You're looking for product prod_123"
}
```
```
--------------------------------
### Access Medusa Services via Dependency Injection
Source: https://github.com/rpuls/medusa-b2b-for-railway/blob/main/backend/src/api/README.md
Demonstrates accessing Medusa's service container to resolve modules and their services. Uses `req.scope.resolve()` with `ModuleRegistrationName.PRODUCT` to get the product module service. This pattern enables accessing business logic, database operations, and other Medusa framework features within route handlers.
```ts
import type { MedusaRequest, MedusaResponse } from "@medusajs/medusa";
import { IProductModuleService } from "@medusajs/framework/types";
import { ModuleRegistrationName } from "@medusajs/framework/utils";
export const GET = async (req: MedusaRequest, res: MedusaResponse) => {
const productModuleService: IProductModuleService = req.scope.resolve(
ModuleRegistrationName.PRODUCT
);
const [, count] = await productModuleService.listAndCount();
res.json({
count,
});
};
```
--------------------------------
### Get Specific Approval Details (Admin) - TypeScript
Source: https://context7.com/rpuls/medusa-b2b-for-railway/llms.txt
Allows system administrators to retrieve detailed information about a specific approval request. It fetches data from the admin approvals endpoint with options to include related cart items and customer details.
```typescript
// Get specific approval details
const detailResponse = await fetch(
'http://localhost:9000/admin/approvals/appr_456?fields=*cart.items,*cart.customer',
{
headers: { 'Authorization': 'Bearer {admin_token}' }
}
);
```
--------------------------------
### Define Multiple HTTP Methods in Single Route
Source: https://github.com/rpuls/medusa-b2b-for-railway/blob/main/backend/src/api/README.md
Demonstrates how to handle different HTTP methods (GET, POST, PUT) within a single route file by exporting functions with method names. Each method handler receives the same MedusaRequest and MedusaResponse parameters. This pattern allows creating RESTful endpoints with proper HTTP verb handling in one location.
```ts
import type { MedusaRequest, MedusaResponse } from "@medusajs/medusa";
export async function GET(req: MedusaRequest, res: MedusaResponse) {
// Handle GET requests
}
export async function POST(req: MedusaRequest, res: MedusaResponse) {
// Handle POST requests
}
export async function PUT(req: MedusaRequest, res: MedusaResponse) {
// Handle PUT requests
}
```
--------------------------------
### Running Custom CLI Scripts in Bash
Source: https://github.com/rpuls/medusa-b2b-for-railway/blob/main/backend/src/scripts/README.md
Execute custom CLI scripts using the medusa exec command, specifying the script file path. Arguments can be passed after the file path for scripts that handle them. Requires npx and the Medusa CLI tool installed. Outputs depend on the script's console statements. No specific limitations beyond script file existence.
```bash
npx medusa exec ./src/scripts/my-script.ts
```
```bash
npx medusa exec ./src/scripts/my-script.ts arg1 arg2
```
--------------------------------
### GET /store/quotes/{quote_id}
Source: https://context7.com/rpuls/medusa-b2b-for-railway/llms.txt
Get the details of a specific quote, including related draft order items and messages.
```APIDOC
## GET /store/quotes/{quote_id}
### Description
Get quote details with preview capabilities.
### Method
GET
### Endpoint
/store/quotes/{quote_id}
### Parameters
#### Path Parameters
- **quote_id** (string) - Required - The ID of the quote to retrieve.
#### Query Parameters
- **fields** (string) - Optional - Comma-separated list of fields to include (e.g., "*draft_order.items,*messages,*order_change").
### Request Example
```http
GET /store/quotes/quote_789?fields=*draft_order.items,*messages,*order_change
```
### Response
#### Success Response (200)
- **quote** (object) - Contains detailed information about the quote.
#### Response Example
```json
{
"quote": {
// ... quote details ...
}
}
```
```
--------------------------------
### Spending Limit Validation Utility
Source: https://context7.com/rpuls/medusa-b2b-for-railway/llms.txt
Backend utility for server-side validation of employee spending limits, including configurable reset windows and calculations based on past orders.
```APIDOC
## Spending Limit Validation
### Description
Server-side validation of employee spending limits with configurable reset windows.
### Method
Utility Functions (not a direct API endpoint)
### Endpoint
N/A
### Parameters
N/A
### Request Example
```typescript
// Example usage in cart validation hook
const cart = {
id: "cart_123",
total: 15000 // $150.00 in cents
};
const customer = {
id: "cus_789",
employee: {
id: "emp_456",
spending_limit: 20000, // $200.00 limit
company: {
spending_limit_reset_frequency: "monthly"
}
},
orders: [
{ id: "order_1", total: 8000, created_at: "2025-11-05T10:00:00Z" },
{ id: "order_2", total: 3000, created_at: "2025-11-08T14:30:00Z" }
]
};
// Calculate spending window (e.g., November 1-30 for monthly)
const spendWindow = getSpendWindow(customer.employee.company);
// Returns: { start: Date(2025-11-01), end: Date(2025-11-10) }
// Calculate total spent in current window
const spent = getOrderTotalInSpendWindow(customer.orders, spendWindow);
// Returns: 11000 ($110.00)
// Check if cart would exceed limit
const exceedsLimit = checkSpendingLimit(cart, customer);
// Returns: true (spent $110 + cart $150 = $260, which is > $200 limit)
```
### Response
N/A
### Notes
Reset frequency options include: "never", "daily", "weekly", "monthly", "yearly".
```
--------------------------------
### Create and Manage Companies using Medusa Store API
Source: https://context7.com/rpuls/medusa-b2b-for-railway/llms.txt
Demonstrates how to create, retrieve, and update company information using the Medusa Store API. It requires a customer token for authorization and sends company details in JSON format.
```typescript
const response = await fetch('http://localhost:9000/store/companies', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': 'Bearer {customer_token}'
},
body: JSON.stringify({
name: "Acme Corporation",
email: "contact@acme.com",
phone: "+1-555-0100",
address: "123 Business St",
city: "San Francisco",
state: "CA",
zip: "94102",
country: "US",
currency_code: "usd",
spending_limit_reset_frequency: "monthly" // Options: never, daily, weekly, monthly, yearly
})
});
const { companies } = await response.json();
// Returns: { companies: [{ id: "comp_123", name: "Acme Corporation", ... }] }
// Retrieve company with employees and approval settings
const companyResponse = await fetch(
'http://localhost:9000/store/companies/comp_123?fields=*employees,*employees.customer,*approval_settings',
{
headers: { 'Authorization': 'Bearer {customer_token}' }
}
);
const { company } = await companyResponse.json();
/* Returns:
{
company: {
id: "comp_123",
name: "Acme Corporation",
employees: [
{ id: "emp_456", spending_limit: 10000, is_admin: true, customer: {...} }
],
approval_settings: {
requires_admin_approval: true,
requires_sales_manager_approval: false
}
}
}
*/
// Update company details
await fetch('http://localhost:9000/store/companies/comp_123', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': 'Bearer {customer_token}'
},
body: JSON.stringify({
name: "Acme Corporation Ltd",
spending_limit_reset_frequency: "weekly"
})
});
```
--------------------------------
### Manage Quote Requests
Source: https://context7.com/rpuls/medusa-b2b-for-railway/llms.txt
Handles quote creation from cart ID and quote acceptance. Requires authentication headers. Returns quote data upon creation or acceptance.
```typescript
export const createQuote = async (cartId: string) => {
const headers = { ...(await getAuthHeaders()) };
const { quote } = await sdk.client.fetch(`/store/quotes`, {
method: "POST",
body: { cart_id: cartId },
headers,
});
return quote;
};
export const acceptQuote = async (quoteId: string) => {
const headers = { ...(await getAuthHeaders()) };
const { quote } = await sdk.client.fetch(
`/store/quotes/${quoteId}/accept`,
{ method: "POST", headers }
);
return quote;
};
```
--------------------------------
### Create Employee Record
Source: https://context7.com/rpuls/medusa-b2b-for-railway/llms.txt
Creates a new employee record for a company with spending limit and admin status. Requires company and customer IDs. Invalidates company cache after creation.
```typescript
export const createEmployee = async (data: {
company_id: string;
customer_id: string;
spending_limit: number;
is_admin: boolean;
}) => {
const { company_id, ...employeeData } = data;
const headers = { ...(await getAuthHeaders()) };
const employee = await sdk.client.fetch(
`/store/companies/${company_id}/employees`,
{ method: "POST", body: employeeData, headers }
);
revalidateTag(await getCacheTag("companies"));
return employee;
};
```
--------------------------------
### Middleware Configuration
Source: https://github.com/rpuls/medusa-b2b-for-railway/blob/main/backend/src/api/README.md
Configuration for applying middleware to specific routes. Shows how to define middleware functions and apply them to route matchers.
```APIDOC
## Middleware Configuration
### Description
Configuration for applying middleware to specific routes. Shows how to define middleware functions and apply them to route matchers.
### Method
CONFIGURATION
### Endpoint
/api/middlewares.ts
### Parameters
#### Configuration Properties
- **matcher** (string|RegExp) - Required - Route pattern to apply middleware to
- **middlewares** (array) - Required - Array of middleware functions to apply
### Request Example
```ts
import { defineMiddlewares } from "@medusajs/medusa";
async function logger(req, res, next) {
console.log("Request received");
next();
}
export default defineMiddlewares({
routes: [
{
matcher: "/store/custom",
middlewares: [logger],
},
],
});
```
### Response
#### Success Response
Returns configured middleware that logs requests to matched routes.
#### Response Example
Middleware is applied transparently to matching routes.
```
--------------------------------
### Customer Actions: Accept, Reject, and Message Quotes (TypeScript)
Source: https://context7.com/rpuls/medusa-b2b-for-railway/llms.txt
Allows customers to interact with merchant quotes. Customers can accept a quote to convert it into an order, reject it with a reason, or add messages to the quote thread. This requires a customer token for authentication.
```typescript
const acceptResponse = await fetch(
'http://localhost:9000/store/quotes/quote_789/accept',
{
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': 'Bearer {customer_token}'
}
}
);
const { quote } = await acceptResponse.json();
const rejectResponse = await fetch(
'http://localhost:9000/store/quotes/quote_789/reject',
{
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': 'Bearer {customer_token}'
},
body: JSON.stringify({
message: "Pricing doesn't meet our budget requirements"
})
}
);
await fetch('http://localhost:9000/store/quotes/quote_789/messages', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': 'Bearer {customer_token}'
},
body: JSON.stringify({
text: "Can we negotiate a better price for quantities over 500 units?"
})
});
```
--------------------------------
### Creating a Product Count CLI Script in TypeScript
Source: https://github.com/rpuls/medusa-b2b-for-railway/blob/main/backend/src/scripts/README.md
This script creates a custom CLI tool that resolves the product module service from the Medusa container and logs the total count of products. It depends on Medusa framework types and utils for module registration. The function receives ExecArgs with a container for dependency injection and outputs the product count via console.log. Limitation: Requires the product module to be registered in the application.
```typescript
import { ExecArgs, IProductModuleService } from "@medusajs/framework/types";
import { ModuleRegistrationName } from "@medusajs/framework/utils";
export default async function myScript({ container }: ExecArgs) {
const productModuleService: IProductModuleService = container.resolve(
ModuleRegistrationName.PRODUCT
);
const [, count] = await productModuleService.listAndCount();
console.log(`You have ${count} product(s)`);
}
```
--------------------------------
### Retrieve Company Data
Source: https://context7.com/rpuls/medusa-b2b-for-railway/llms.txt
Fetches detailed company information including employees and approval settings. Requires authentication headers and uses caching options. Returns company data with extended fields.
```typescript
"use server"
import { sdk } from "@/lib/config";
import { getAuthHeaders, getCacheOptions } from "@/lib/data/cookies";
import { revalidateTag } from "next/cache";
export const retrieveCompany = async (companyId: string) => {
const headers = { ...(await getAuthHeaders()) };
const next = { ...(await getCacheOptions("companies")) };
const { company } = await sdk.client.fetch(
`/store/companies/${companyId}`,
{
query: {
fields: "+spending_limit_reset_frequency,*employees.customer,*approval_settings"
},
method: "GET",
headers,
next,
}
);
return company;
};
```
--------------------------------
### List All Approvals (Admin) - TypeScript
Source: https://context7.com/rpuls/medusa-b2b-for-railway/llms.txt
Provides an endpoint for system administrators to view all approvals across different companies. It fetches data from the admin approvals endpoint, allowing for comprehensive oversight.
```typescript
// Admin API - List all approvals across companies
const adminApprovalsResponse = await fetch(
'http://localhost:9000/admin/approvals?limit=50&offset=0',
{
headers: { 'Authorization': 'Bearer {admin_token}' }
}
);
```
--------------------------------
### Running Integration Tests for Custom Endpoints in TypeScript
Source: https://github.com/rpuls/medusa-b2b-for-railway/blob/main/backend/integration-tests/http/README.md
This snippet uses medusaIntegrationTestRunner from medusa-test-utils to define and execute integration tests for API routes. It tests a GET /store/custom endpoint, verifying the response status is 200 and contains a specific message. Dependencies: medusa-test-utils package; inputs: test suite configuration; outputs: test results; limitations: requires a running Medusa server for API calls.
```typescript
import { medusaIntegrationTestRunner } from "medusa-test-utils"
medusaIntegrationTestRunner({
testSuite: ({ api, getContainer }) => {
describe("Custom endpoints", () => {
describe("GET /store/custom", () => {
it("returns correct message", async () => {
const response = await api.get(
`/store/custom`
)
expect(response.status).toEqual(200)
expect(response.data).toHaveProperty("message")
expect(response.data.message).toEqual("Hello, World!")
})
})
})
}
})
```
--------------------------------
### Configure Company Approval Settings (TypeScript)
Source: https://context7.com/rpuls/medusa-b2b-for-railway/llms.txt
Allows company administrators to configure order approval requirements at the company level. This includes setting whether admin or sales manager approval is needed for orders. It provides both a store-level API for customer-facing actions and an admin API for backend management.
```typescript
// Update company approval settings (Company admin only)
const settingsResponse = await fetch(
'http://localhost:9000/store/companies/comp_123/approval-settings',
{
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': 'Bearer {company_admin_token}'
},
body: JSON.stringify({
requires_admin_approval: true, // Employees must get admin approval
requires_sales_manager_approval: false // No merchant approval needed
})
}
);
// Admin API version (more control)
await fetch('http://localhost:9000/admin/companies/comp_123/approval-settings', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': 'Bearer {admin_token}'
},
body: JSON.stringify({
requires_admin_approval: true,
requires_sales_manager_approval: true // Require both levels
})
});
// Storefront integration example (Next.js server action)
import { updateApprovalSettings } from '@/lib/data/companies';
const handleToggleApproval = async (companyId: string, enabled: boolean) => {
try {
await updateApprovalSettings(companyId, enabled);
console.log('Approval settings updated');
} catch (error) {
console.error('Failed to update settings:', error);
}
};
```
--------------------------------
### Quote Management API
Source: https://context7.com/rpuls/medusa-b2b-for-railway/llms.txt
Endpoints for managing quotes, including creation and acceptance.
```APIDOC
## POST /store/quotes
### Description
Creates a new quote based on the items in a shopping cart.
### Method
POST
### Endpoint
/store/quotes
### Parameters
#### Request Body
- **cart_id** (string) - Required - The ID of the cart to create a quote from.
### Request Example
```json
{
"cart_id": "cart_12345"
}
```
### Response
#### Success Response (200)
- **quote** (object) - The newly created quote object.
- **id** (string) - The ID of the created quote.
- **cart_id** (string) - The ID of the associated cart.
#### Response Example
```json
{
"quote": {
"id": "quote_abcdef",
"cart_id": "cart_12345",
"status": "created"
}
}
```
```
```APIDOC
## POST /store/quotes/{quoteId}/accept
### Description
Accepts a specific quote, converting it into an order.
### Method
POST
### Endpoint
/store/quotes/{quoteId}/accept
### Parameters
#### Path Parameters
- **quoteId** (string) - Required - The ID of the quote to accept.
### Response
#### Success Response (200)
- **quote** (object) - The accepted quote object, now potentially representing an order.
- **id** (string) - The ID of the accepted quote.
- **status** (string) - The updated status of the quote (e.g., "accepted").
#### Response Example
```json
{
"quote": {
"id": "quote_abcdef",
"status": "accepted"
}
}
```
```
--------------------------------
### Bulk Add to Cart API
Source: https://context7.com/rpuls/medusa-b2b-for-railway/llms.txt
Allows B2B customers to add multiple product variants to their cart simultaneously, streamlining the bulk ordering process.
```APIDOC
## POST /store/carts/{cart_id}/line-items/bulk
### Description
Adds multiple product variants to a shopping cart in a single API request. This is optimized for B2B bulk ordering scenarios.
### Method
POST
### Endpoint
/store/carts/{cart_id}/line-items/bulk
### Parameters
#### Path Parameters
- **cart_id** (string) - Required - The ID of the shopping cart to which items will be added.
#### Query Parameters
None
#### Request Body
- **line_items** (array) - Required - An array of objects, where each object specifies a product variant and the desired quantity.
- **variant_id** (string) - Required - The ID of the product variant.
- **quantity** (integer) - Required - The number of units for the variant.
### Request Example
```json
{
"line_items": [
{ "variant_id": "variant_red_small", "quantity": 50 },
{ "variant_id": "variant_red_medium", "quantity": 75 },
{ "variant_id": "variant_red_large", "quantity": 100 },
{ "variant_id": "variant_blue_small", "quantity": 25 },
{ "variant_id": "variant_blue_medium", "quantity": 50 }
]
}
```
### Response
#### Success Response (200)
- **cart** (object) - The updated cart object, including the newly added items and updated totals.
- **id** (string) - The cart ID.
- **items** (array) - An array of line items in the cart.
- **total** (integer) - The total price of the cart.
- **subtotal** (integer) - The subtotal price of the cart.
#### Response Example
```json
{
"cart": {
"id": "cart_123",
"items": [
{ "id": "item_1", "variant_id": "variant_red_small", "quantity": 50, ... },
{ "id": "item_2", "variant_id": "variant_red_medium", "quantity": 75, ... },
{ "id": "item_3", "variant_id": "variant_red_large", "quantity": 100, ... },
{ "id": "item_4", "variant_id": "variant_blue_small", "quantity": 25, ... },
{ "id": "item_5", "variant_id": "variant_blue_medium", "quantity": 50, ... }
],
"total": 45000,
"subtotal": 45000
}
}
```
```
--------------------------------
### Company Management API
Source: https://context7.com/rpuls/medusa-b2b-for-railway/llms.txt
Provides endpoints for retrieving company details and managing employees within the B2B context.
```APIDOC
## GET /store/companies/{companyId}
### Description
Retrieves the details of a specific company, including its spending limit, reset frequency, employees, and approval settings.
### Method
GET
### Endpoint
/store/companies/{companyId}
### Parameters
#### Path Parameters
- **companyId** (string) - Required - The ID of the company to retrieve.
#### Query Parameters
- **fields** (string) - Optional - Specifies which fields to include in the response. Example: `spending_limit_reset_frequency,employees.customer,approval_settings`
### Response
#### Success Response (200)
- **company** (object) - Contains the company details.
- **spending_limit** (number) - The spending limit for the company.
- **spending_limit_reset_frequency** (string) - The frequency at which the spending limit resets.
- **employees** (array) - A list of employees associated with the company.
- **approval_settings** (object) - Settings related to approval workflows.
#### Response Example
```json
{
"company": {
"id": "comp_abc123",
"name": "Example Corp",
"spending_limit": 5000,
"spending_limit_reset_frequency": "monthly",
"employees": [
{
"id": "emp_xyz789",
"customer_id": "cust_pqr456",
"is_admin": true
}
],
"approval_settings": {
"require_approval": true,
"approved_limit": 1000
}
}
}
```
```
```APIDOC
## POST /store/companies/{companyId}/employees
### Description
Creates a new employee record for a specified company.
### Method
POST
### Endpoint
/store/companies/{companyId}/employees
### Parameters
#### Path Parameters
- **companyId** (string) - Required - The ID of the company to add the employee to.
#### Request Body
- **customer_id** (string) - Required - The ID of the customer to be added as an employee.
- **spending_limit** (number) - Required - The spending limit for this employee.
- **is_admin** (boolean) - Required - Whether the employee has administrative privileges.
### Request Example
```json
{
"customer_id": "cust_pqr456",
"spending_limit": 1000,
"is_admin": false
}
```
### Response
#### Success Response (200)
- **employee** (object) - The newly created employee record.
- **id** (string) - The ID of the created employee.
- **company_id** (string) - The ID of the company the employee belongs to.
- **customer_id** (string) - The ID of the customer associated with the employee.
- **spending_limit** (number) - The spending limit assigned to the employee.
- **is_admin** (boolean) - Indicates if the employee is an admin.
#### Response Example
```json
{
"employee": {
"id": "emp_def012",
"company_id": "comp_abc123",
"customer_id": "cust_pqr456",
"spending_limit": 1000,
"is_admin": false
}
}
```
```
--------------------------------
### Create Quote Request from Cart (Customer API - TypeScript)
Source: https://context7.com/rpuls/medusa-b2b-for-railway/llms.txt
API endpoints for customers to create quote requests from their carts for price negotiation. It includes methods to POST a new quote request, list existing quotes with pagination, retrieve specific quote details, and preview quote changes. Requires customer authentication via a Bearer token.
```typescript
// Create quote request from cart (Customer)
const quoteResponse = await fetch('http://localhost:9000/store/quotes', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': 'Bearer {customer_token}'
},
body: JSON.stringify({
cart_id: "cart_123"
})
});
const { quote } = await quoteResponse.json();
/* Returns:
{
quote: {
id: "quote_789",
status: "pending_merchant",
customer_id: "cus_456",
draft_order_id: "order_draft_123",
order_change_id: "oc_456",
cart_id: "cart_123",
created_at: "2025-11-10T10:00:00Z"
}
}
*/
// The workflow creates:
// 1. Draft order from cart items
// 2. Order edit/change for pricing modifications
// 3. Quote linking all entities
// List customer quotes with pagination
const quotesResponse = await fetch(
'http://localhost:9000/store/quotes?limit=10&offset=0&fields=*messages,*draft_order.items',
{
headers: { 'Authorization': 'Bearer {customer_token}' }
}
);
const { quotes, count, offset, limit } = await quotesResponse.json();
// Get quote details with preview
const quoteDetailResponse = await fetch(
'http://localhost:9000/store/quotes/quote_789?fields=*draft_order.items,*messages,*order_change',
{
headers: { 'Authorization': 'Bearer {customer_token}' }
}
);
// Preview quote changes before accepting
const previewResponse = await fetch(
'http://localhost:9000/store/quotes/quote_789/preview',
{
headers: { 'Authorization': 'Bearer {customer_token}' }
}
);
const { order_preview } = await previewResponse.json();
// Shows updated prices, totals, and line items
```
--------------------------------
### Configure MinIO Module in Medusa
Source: https://github.com/rpuls/medusa-b2b-for-railway/blob/main/backend/src/modules/minio-file/README.md
JavaScript configuration object for registering the MinIO file provider module in medusa-config.js. Automatically loaded when environment variables are present. Specifies module resolution path and passes connection options including endpoint, credentials, and bucket configuration.
```javascript
{
resolve: './src/modules/minio-file',
options: {
endPoint: MINIO_ENDPOINT,
accessKey: MINIO_ACCESS_KEY,
secretKey: MINIO_SECRET_KEY,
bucket: MINIO_BUCKET // Optional, defaults to 'medusa-media'
}
}
```
--------------------------------
### Company Management API
Source: https://context7.com/rpuls/medusa-b2b-for-railway/llms.txt
This API allows for the creation and management of companies with employee hierarchies, spending limits, and approval settings. It provides endpoints to create, retrieve, update, and manage company details.
```APIDOC
# Company Management API
### Description
Brief description of what this endpoint does
### Method
POST
### Endpoint
/store/companies
### Parameters
#### Path Parameters
None
#### Query Parameters
None
#### Request Body
- `name` (string) - Required - Company name
- `email` (string) - Required - Company email
- `phone` (string) - Optional - Company phone number
- `address` (string) - Optional - Company address
- `city` (string) - Optional - Company city
- `state` (string) - Optional - Company state
- `zip` (string) - Optional - Company zip code
- `country` (string) - Optional - Company country
- `currency_code` (string) - Optional - Currency code
- `spending_limit_reset_frequency` (string) - Optional - Spending limit reset frequency (never, daily, weekly, monthly, yearly)
### Request Example
```json
{
"name": "Acme Corporation",
"email": "contact@acme.com",
"phone": "+1-555-0100",
"address": "123 Business St",
"city": "San Francisco",
"state": "CA",
"zip": "94102",
"country": "US",
"currency_code": "usd",
"spending_limit_reset_frequency": "monthly"
}
```
### Response
#### Success Response (200)
- `companies` (array) - An array of company objects.
#### Response Example
```json
{
"companies": [{
"id": "comp_123",
"name": "Acme Corporation"
}]
}
```
## GET /store/companies/{company_id}
### Description
Retrieve company with employees and approval settings.
### Method
GET
### Endpoint
/store/companies/{company_id}?fields=*
### Parameters
#### Path Parameters
- `company_id` (string) - Required - The ID of the company to retrieve
#### Query Parameters
- `fields` (string) - Optional - Specify which fields to return
#### Request Body
None
### Request Example
```typescript
const companyResponse = await fetch(
'http://localhost:9000/store/companies/comp_123?fields=*
',
{
headers: { 'Authorization': 'Bearer {customer_token}' }
}
);
```
### Response
#### Success Response (200)
- `company` (object) - The company object with employees and approval settings.
#### Response Example
```json
{
"company": {
"id": "comp_123",
"name": "Acme Corporation",
"employees": [{
"id": "emp_456",
"spending_limit": 10000,
"is_admin": true,
"customer": { ... }
}],
"approval_settings": {
"requires_admin_approval": true,
"requires_sales_manager_approval": false
}
}
}
```
## POST /store/companies/{company_id}
### Description
Update company details.
### Method
POST
### Endpoint
/store/companies/{company_id}
### Parameters
#### Path Parameters
- `company_id` (string) - Required - The ID of the company to update
#### Query Parameters
None
#### Request Body
- `name` (string) - Optional - New company name
- `spending_limit_reset_frequency` (string) - Optional - New spending limit reset frequency
### Request Example
```typescript
await fetch('http://localhost:9000/store/companies/comp_123', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': 'Bearer {customer_token}'
},
body: JSON.stringify({
name: "Acme Corporation Ltd",
spending_limit_reset_frequency: "weekly"
})
});
```
### Response
#### Success Response (200)
None
```
--------------------------------
### POST /store/quotes
Source: https://context7.com/rpuls/medusa-b2b-for-railway/llms.txt
Create a quote request from a customer's cart. This initiates a workflow involving draft orders and order edits for price negotiation.
```APIDOC
## POST /store/quotes
### Description
Create quote requests from carts for price negotiation with merchants.
### Method
POST
### Endpoint
/store/quotes
### Parameters
#### Request Body
- **cart_id** (string) - Required - The ID of the cart to create a quote from.
### Request Example
```json
{
"cart_id": "cart_123"
}
```
### Response
#### Success Response (200)
- **quote** (object) - Contains details of the created quote.
- **id** (string) - The unique identifier for the quote.
- **status** (string) - The current status of the quote (e.g., "pending_merchant").
- **customer_id** (string) - The ID of the customer requesting the quote.
- **draft_order_id** (string) - The ID of the associated draft order.
- **order_change_id** (string) - The ID of the order change associated with the quote.
- **cart_id** (string) - The ID of the original cart.
- **created_at** (string) - The timestamp when the quote was created.
#### Response Example
```json
{
"quote": {
"id": "quote_789",
"status": "pending_merchant",
"customer_id": "cus_456",
"draft_order_id": "order_draft_123",
"order_change_id": "oc_456",
"cart_id": "cart_123",
"created_at": "2025-11-10T10:00:00Z"
}
}
```
```
--------------------------------
### Configure Middleware for Medusa API Routes
Source: https://github.com/rpuls/medusa-b2b-for-railway/blob/main/backend/src/api/README.md
Shows how to apply middleware to specific routes using the `/api/middlewares.ts` file. Uses `defineMiddlewares` to configure route matchers and associate middleware functions. The matcher can be a string or regular expression, and multiple middleware functions can be applied to a single route.
```ts
import { defineMiddlewares } from "@medusajs/medusa";
import type {
MedusaRequest,
MedusaResponse,
MedusaNextFunction,
} from "@medusajs/medusa";
async function logger(
req: MedusaRequest,
res: MedusaResponse,
next: MedusaNextFunction
) {
console.log("Request received");
next();
}
export default defineMiddlewares({
routes: [
{
matcher: "/store/custom",
middlewares: [logger],
},
],
});
```