Welcome to the Jose Madrid Salsa developer docs — explore features, APIs, and deployment guides.
Jose Madrid SalsaJMS Docs

API Integration

Integrating with the Jose Madrid Salsa REST API

API Integration

This guide covers how to integrate with the Jose Madrid Salsa API, including authentication, available endpoints, rate limiting, and response formats.

API Overview

The API is built with Next.js Route Handlers in the app/api/ directory. All endpoints return JSON responses and use standard HTTP status codes.

Authentication

Session-Based (Web App)

The web app uses NextAuth.js JWT sessions. Session tokens are sent automatically via cookies.

Partner API Keys

For external integrations, use Partner API keys:

# Create a partner API key
npm run api-keys:create

Partner API keys are stored in the PartnerApiKey table and can be managed at /admin/settings.

Products API

List Products

GET /api/products

Query Parameters:

ParameterTypeDescription
heatLevelstringFilter by heat level: MILD, MEDIUM, HOT, EXTRA_HOT
searchstringCase-insensitive search in name and description
featuredtrueOnly featured products
categoriesstringComma-separated category slugs
tagsstringComma-separated tag slugs
inStocktrue/1Only in-stock products
takenumberLimit results
skipnumberOffset for pagination
sortOrderasc/descSort direction

Example:

const response = await fetch('/api/products?heatLevel=HOT&featured=true&take=10')
const products = await response.json()

Response:

[
  {
    "id": "clxx...",
    "name": "Ghost of Clovis",
    "slug": "ghost-of-clovis",
    "description": "Smoky ghost peppers...",
    "price": 9.49,
    "compareAtPrice": 11.49,
    "featuredImage": "/images/new-products/ghost-clovis.png",
    "images": [...],
    "heatLevel": "HOT",
    "sku": "JMS-HOT-001",
    "inventory": 80,
    "isFeatured": true,
    "tags": ["spicy", "ghost-pepper"],
    "nutritionalInfo": { ... }
  }
]

Fallback Products

If the database is unavailable, the products API returns a hardcoded set of mock products to keep the storefront functional.

Get Single Product

GET /api/products/[id]

Returns a single product with full details including variants, nutritional info, and ingredients.

Orders API

Create Order

POST /api/orders

Requires authentication. Request body:

{
  "cartItemIds": ["clxx...", "clxx..."],
  "shippingAddress": {
    "address1": "123 Main St",
    "city": "San Francisco",
    "state": "CA",
    "postalCode": "94111",
    "country": "US"
  },
  "notes": "Leave at front door"
}

Response:

{
  "clientSecret": "pi_xxx_secret_xxx",
  "orderId": "clxx...",
  "orderNumber": "JMS-20240115-A1B2C3D4",
  "amount": 28.33
}

The clientSecret is used to confirm the Stripe PaymentIntent on the client.

List My Orders

GET /api/orders?status=SHIPPED&sortOrder=desc

Returns orders for the authenticated user.

Rate Limiting

All API endpoints are rate-limited. Exceeding the limit returns a 429 Too Many Requests response.

Endpoint TypeLimit
General API100 requests / minute
AI Chat20 requests / minute (50 for authenticated)
Login5 attempts / 15 minutes
Password Reset3 requests / hour

Rate limit information is included in response headers:

X-RateLimit-Limit: 100
X-RateLimit-Remaining: 95
X-RateLimit-Reset: 45

Error Handling

All API errors follow a consistent format:

{
  "error": "Human-readable error message",
  "details": { ... }
}

HTTP Status Codes:

CodeMeaning
200Success
400Validation error (check details)
401Not authenticated
403Insufficient permissions
429Rate limit exceeded
500Server error

Input Validation

All POST/PUT endpoints validate input with Zod schemas. Invalid requests return 400 with a flattened error object:

{
  "error": "Invalid order payload",
  "details": {
    "fieldErrors": {
      "cartItemIds": ["Required"]
    },
    "formErrors": []
  }
}

Webhooks

Stripe Webhooks

POST /api/webhooks/stripe

Handles payment events (payment succeeded, failed, refunded). Requires STRIPE_WEBHOOK_SECRET for signature verification.

Shopify Webhooks

POST /api/webhooks/shopify

Handles order sync events from Shopify.

Working with the API in Code

Server-Side (Server Components / Actions)

Use Prisma directly instead of calling the API:

import { prisma } from '@/lib/prisma'

const products = await prisma.product.findMany({
  where: { isActive: true },
})

Client-Side (React Components)

Use fetch to call API routes:

'use client'

import { useEffect, useState } from 'react'

function ProductList() {
  const [products, setProducts] = useState([])

  useEffect(() => {
    fetch('/api/products?featured=true')
      .then(res => res.json())
      .then(setProducts)
  }, [])

  return (/* render products */)
}

External Services

For external integrations, include the API key:

const response = await fetch('https://josemadrid.net/api/products', {
  headers: {
    'Authorization': `Bearer ${apiKey}`,
    'Content-Type': 'application/json',
  },
})

Admin API

Admin endpoints under /api/admin/ require ADMIN, DEVELOPER, or STAFF roles. These provide CRUD operations for:

  • Products (/api/admin/products)
  • Orders (/api/admin/orders)
  • Users (/api/admin/users)
  • Categories (/api/admin/categories)
  • Email templates (/api/admin/email-templates)
  • Settings (/api/admin/settings)

How is this guide?

Edit on GitHub

Last updated on

On this page