### 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], }, ], }); ```