Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/ALEJ4NDRO2025/urban-store/llms.txt

Use this file to discover all available pages before exploring further.

Urban Store’s product catalog is stored in a MongoDB products collection and managed entirely through the /api/products/ REST API. All write operations — create, update, and delete — are restricted to admin users via the IsAdminMongo permission class. Read operations (browsing and filtering the catalog) are public. Each product supports granular per-variant inventory tracking through a stock_by_variant dictionary, and images are stored as Cloudinary URLs.

Product Schema

The Product MongoEngine document exposes the following fields:
FieldTypeRequiredDescription
nameString (max 200)YesDisplay name of the product
slugString (unique)YesURL-safe identifier used in all API paths
descriptionStringNoLong-form product description
priceDecimal (2 places)YesUnit price
categoryStringYesProduct category, e.g. camisetas, accesorios
stockIntegerNoGeneral stock fallback (default 0)
sizesList[String]NoAvailable sizes, e.g. ["S", "M", "L", "XL"]
colorsList[String]NoAvailable colors, e.g. ["negro", "blanco", "rojo"]
stock_by_variantDictNoPer-variant inventory map (see below)
imagesList[URL]NoCloudinary secure URLs
is_activeBooleanNotrue = visible in public catalog (default true)
created_atDateTimeSet automatically on creation

Per-Variant Stock Format

The stock_by_variant dictionary maps composite keys in the format "<size>|<color>" to integer quantities. This lets you track inventory for each exact combination independently:
{
  "stock_by_variant": {
    "S|negro":   10,
    "M|negro":    5,
    "M|blanco":   3,
    "L|rojo":     0
  }
}
A quantity of 0 means the variant is sold out. The key uses a pipe character | as the separator between size and color.

Creating a Product

POST /api/products/
Authorization: Bearer <admin_token>
Content-Type: application/json
Request body:
{
  "name": "Camiseta Oversize Negra",
  "slug": "camiseta-oversize-negra",
  "description": "Camiseta de algodón 100% oversize con corte urbano.",
  "price": "79900.00",
  "category": "camisetas",
  "stock": 0,
  "sizes": ["S", "M", "L", "XL"],
  "colors": ["negro", "blanco"],
  "stock_by_variant": {
    "S|negro":  10,
    "M|negro":   8,
    "L|negro":   5,
    "XL|negro":  2,
    "S|blanco":  6,
    "M|blanco":  4
  },
  "images": [
    "https://res.cloudinary.com/your-cloud/image/upload/v1234567890/products/camiseta-oversize-negra-1.jpg",
    "https://res.cloudinary.com/your-cloud/image/upload/v1234567890/products/camiseta-oversize-negra-2.jpg"
  ],
  "is_active": true
}
A successful creation returns 201 Created:
{
  "message": "Producto creado exitosamente",
  "slug": "camiseta-oversize-negra",
  "name": "Camiseta Oversize Negra"
}

Editing a Product

Partial updates are fully supported — send only the fields you want to change.
PUT /api/products/<slug>/
Authorization: Bearer <admin_token>
Content-Type: application/json
Example — updating stock for two variants:
{
  "stock_by_variant": {
    "S|negro":  3,
    "M|negro":  0
  }
}
Example — disabling a product:
{
  "is_active": false
}

Deleting a Product

DELETE /api/products/<slug>/
Authorization: Bearer <admin_token>
This is a hard delete. The document is permanently removed from MongoDB and cannot be recovered.
The slug field is effectively immutable once set. Changing a product’s slug via a PUT request will cause all existing URLs, bookmarks, and cart references that point to the old slug to break immediately, since every product API route is keyed on slug. If you need to rename a product, update name only and leave slug unchanged.

Soft Deactivation

To hide a product from the public catalog without permanently deleting it, set is_active to false:
PUT /api/products/<slug>/
Authorization: Bearer <admin_token>
Content-Type: application/json

{
  "is_active": false
}
The public GET /api/products/ endpoint filters to is_active=true only, so deactivated products disappear from the storefront immediately. They can be re-activated at any time by setting is_active back to true.

Image Management

Product images are stored as Cloudinary secure URLs inside the images list on each product document. Urban Store does not upload images directly — you upload your assets to Cloudinary first and then reference the resulting URLs.
1

Upload to Cloudinary

Log in to your Cloudinary dashboard and upload the product image via the Media Library or the Cloudinary Upload widget. Copy the Secure URL (https://res.cloudinary.com/...).
2

Add the URL to the product

Include the URL in the images array when creating a product, or send a PUT request to append or replace images on an existing product:
{
  "images": [
    "https://res.cloudinary.com/your-cloud/image/upload/v1234567890/products/product-front.jpg",
    "https://res.cloudinary.com/your-cloud/image/upload/v1234567890/products/product-back.jpg"
  ]
}

Admin Permission

Write access to the product endpoints is enforced by the IsAdminMongo permission class defined in products/views.py. It manually decodes the JWT from the Authorization header and checks the is_admin field in the payload:
class IsAdminMongo(BasePermission):
    def has_permission(self, request, view):
        auth = request.headers.get('Authorization', '')
        if not auth.startswith('Bearer '):
            return False
        token = auth.split(' ')[1]
        try:
            payload = jwt.decode(token, settings.SECRET_KEY, algorithms=['HS256'])
            return payload.get('is_admin', False)
        except:
            return False
Every admin write request must include the header:
Authorization: Bearer <your_admin_jwt>
Requests that omit the header, use an expired token, or belong to a non-admin user receive 403 Forbidden.

Build docs developers (and LLMs) love