{
  "openapi": "3.1.0",
  "info": {
    "title": "canvaWall API",
    "version": "0.1.0",
    "description": "Backend API for canvaWall — a Canva-like design/wall builder micro-SaaS. Provides CRUD for user designs and a read-only catalog of design templates. Auth (Clerk) is enforced on design write routes (task-004) via RS256/JWKS verification; billing checkout is user-keyed when authenticated. Payments (Stripe) are integrated (task-003) via /api/billing/*.",
    "contact": {
      "name": "BoomerDev",
      "url": "https://github.com/BoomerDev"
    }
  },
  "servers": [
    {
      "url": "https://api.canvawall.theboomer.dev",
      "description": "Producción"
    }
  ],
  "tags": [
    {
      "name": "health",
      "description": "Service health and liveness"
    },
    {
      "name": "designs",
      "description": "User-created designs (CRUD)"
    },
    {
      "name": "templates",
      "description": "Read-only catalog of design templates"
    },
    {
      "name": "billing",
      "description": "Stripe payments: plan catalog, checkout, webhooks"
    }
  ],
  "paths": {
    "/health": {
      "get": {
        "tags": [
          "health"
        ],
        "summary": "Health check",
        "operationId": "getHealth",
        "responses": {
          "200": {
            "description": "Service is healthy",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Health"
                }
              }
            }
          }
        }
      }
    },
    "/api/designs": {
      "get": {
        "tags": [
          "designs"
        ],
        "summary": "List designs",
        "operationId": "listDesigns",
        "parameters": [
          {
            "name": "status",
            "in": "query",
            "description": "Filter by status",
            "schema": {
              "type": "string",
              "enum": [
                "draft",
                "published",
                "archived"
              ]
            }
          },
          {
            "name": "limit",
            "in": "query",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 100,
              "default": 50
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Array of designs",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/Design"
                  }
                }
              }
            }
          }
        }
      },
      "post": {
        "tags": [
          "designs"
        ],
        "summary": "Create a design",
        "operationId": "createDesign",
        "security": [
          {
            "BearerAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/DesignInput"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Design created",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Design"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        }
      }
    },
    "/api/designs/{id}": {
      "parameters": [
        {
          "name": "id",
          "in": "path",
          "required": true,
          "schema": {
            "type": "string"
          }
        }
      ],
      "get": {
        "tags": [
          "designs"
        ],
        "summary": "Get a design by id",
        "operationId": "getDesign",
        "responses": {
          "200": {
            "description": "Design found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Design"
                }
              }
            }
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      },
      "put": {
        "tags": [
          "designs"
        ],
        "summary": "Update a design",
        "operationId": "updateDesign",
        "security": [
          {
            "BearerAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/DesignInput"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Design updated",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Design"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      },
      "delete": {
        "tags": [
          "designs"
        ],
        "summary": "Delete a design",
        "operationId": "deleteDesign",
        "security": [
          {
            "BearerAuth": []
          }
        ],
        "responses": {
          "204": {
            "description": "Design deleted"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      }
    },
    "/api/templates": {
      "get": {
        "tags": [
          "templates"
        ],
        "summary": "List templates",
        "operationId": "listTemplates",
        "responses": {
          "200": {
            "description": "Array of templates",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/Template"
                  }
                }
              }
            }
          }
        }
      }
    },
    "/api/templates/{id}": {
      "parameters": [
        {
          "name": "id",
          "in": "path",
          "required": true,
          "schema": {
            "type": "string"
          }
        }
      ],
      "get": {
        "tags": [
          "templates"
        ],
        "summary": "Get a template by id",
        "operationId": "getTemplate",
        "responses": {
          "200": {
            "description": "Template found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Template"
                }
              }
            }
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      }
    },
    "/api/billing/plans": {
      "get": {
        "tags": [
          "billing"
        ],
        "summary": "List pricing plans",
        "operationId": "listPlans",
        "responses": {
          "200": {
            "description": "Catalog of pricing plans",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "plans": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Plan"
                      }
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/api/billing/checkout": {
      "post": {
        "tags": [
          "billing"
        ],
        "summary": "Create a Stripe Checkout session",
        "operationId": "createCheckout",
        "security": [
          {
            "BearerAuth": []
          }
        ],
        "description": "Auth is OPTIONAL: an authenticated caller keys the resulting subscription ledger by their Clerk user id (via Stripe client_reference_id); anonymous calls are accepted and keyed by Stripe customer id.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CheckoutRequest"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Checkout session created",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CheckoutSession"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/PaymentRequired"
          }
        }
      }
    },
    "/api/billing/webhook": {
      "post": {
        "tags": [
          "billing"
        ],
        "summary": "Receive Stripe webhook events",
        "operationId": "handleWebhook",
        "description": "Verifies the Stripe-Signature header (HMAC-SHA256) and applies the event to the subscription ledger. Requires the raw body.",
        "responses": {
          "200": {
            "description": "Webhook received and verified",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/WebhookResponse"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          }
        }
      }
    }
  },
  "components": {
    "securitySchemes": {
      "BearerAuth": {
        "type": "http",
        "scheme": "bearer",
        "bearerFormat": "JWT",
        "description": "Clerk session JWT. Send as `Authorization: Bearer <token>`. Required on design write routes; optional on billing checkout."
      }
    },
    "schemas": {
      "Health": {
        "type": "object",
        "properties": {
          "status": {
            "type": "string",
            "examples": [
              "ok"
            ]
          },
          "uptime": {
            "type": "number"
          },
          "timestamp": {
            "type": "string",
            "format": "date-time"
          },
          "version": {
            "type": "string"
          }
        }
      },
      "DesignElement": {
        "type": "object",
        "properties": {
          "type": {
            "type": "string",
            "examples": [
              "text",
              "image",
              "shape"
            ]
          },
          "x": {
            "type": "number"
          },
          "y": {
            "type": "number"
          },
          "width": {
            "type": "number"
          },
          "height": {
            "type": "number"
          },
          "props": {
            "type": "object",
            "additionalProperties": true
          }
        }
      },
      "Design": {
        "type": "object",
        "required": [
          "id",
          "name",
          "status",
          "createdAt",
          "updatedAt"
        ],
        "properties": {
          "id": {
            "type": "string"
          },
          "ownerId": {
            "type": [
              "string",
              "null"
            ],
            "description": "Authenticated user id (Clerk `sub`); null for anonymous/legacy designs."
          },
          "name": {
            "type": "string"
          },
          "templateId": {
            "type": [
              "string",
              "null"
            ]
          },
          "status": {
            "type": "string",
            "enum": [
              "draft",
              "published",
              "archived"
            ]
          },
          "elements": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/DesignElement"
            }
          },
          "createdAt": {
            "type": "string",
            "format": "date-time"
          },
          "updatedAt": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "DesignInput": {
        "type": "object",
        "required": [
          "name"
        ],
        "properties": {
          "name": {
            "type": "string",
            "minLength": 1,
            "maxLength": 120
          },
          "templateId": {
            "type": [
              "string",
              "null"
            ]
          },
          "status": {
            "type": "string",
            "enum": [
              "draft",
              "published",
              "archived"
            ],
            "default": "draft"
          },
          "elements": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/DesignElement"
            }
          }
        }
      },
      "Template": {
        "type": "object",
        "required": [
          "id",
          "name",
          "category"
        ],
        "properties": {
          "id": {
            "type": "string"
          },
          "name": {
            "type": "string"
          },
          "description": {
            "type": "string"
          },
          "category": {
            "type": "string",
            "examples": [
              "social",
              "poster",
              "logo",
              "story"
            ]
          },
          "thumbnail": {
            "type": [
              "string",
              "null"
            ]
          }
        }
      },
      "Plan": {
        "type": "object",
        "required": [
          "id",
          "name",
          "price",
          "currency",
          "interval"
        ],
        "properties": {
          "id": {
            "type": "string",
            "examples": [
              "free",
              "pro",
              "team"
            ]
          },
          "name": {
            "type": "string"
          },
          "price": {
            "type": "number",
            "description": "Price in major units per interval (0 for free)"
          },
          "currency": {
            "type": "string",
            "examples": [
              "usd"
            ]
          },
          "interval": {
            "type": "string",
            "examples": [
              "month"
            ]
          },
          "stripePriceId": {
            "type": [
              "string",
              "null"
            ],
            "description": "Stripe Price ID (null for free plan)"
          },
          "features": {
            "type": "array",
            "items": {
              "type": "string"
            }
          }
        }
      },
      "CheckoutRequest": {
        "type": "object",
        "required": [
          "planId"
        ],
        "properties": {
          "planId": {
            "type": "string",
            "examples": [
              "pro",
              "team"
            ]
          },
          "successUrl": {
            "type": "string",
            "format": "uri"
          },
          "cancelUrl": {
            "type": "string",
            "format": "uri"
          },
          "email": {
            "type": "string",
            "format": "email"
          }
        }
      },
      "CheckoutSession": {
        "type": "object",
        "required": [
          "id",
          "url"
        ],
        "properties": {
          "id": {
            "type": "string",
            "description": "Stripe Checkout Session id"
          },
          "url": {
            "type": "string",
            "format": "uri",
            "description": "Hosted checkout URL"
          }
        }
      },
      "WebhookResponse": {
        "type": "object",
        "required": [
          "received"
        ],
        "properties": {
          "received": {
            "type": "boolean"
          },
          "action": {
            "type": [
              "string",
              "null"
            ],
            "examples": [
              "subscription_activated",
              "subscription_deactivated",
              "ignored"
            ]
          }
        }
      },
      "Error": {
        "type": "object",
        "required": [
          "error"
        ],
        "properties": {
          "error": {
            "type": "string"
          },
          "detail": {
            "type": "string"
          }
        }
      }
    },
    "responses": {
      "BadRequest": {
        "description": "Bad request",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      },
      "Unauthorized": {
        "description": "Missing or invalid authentication token",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      },
      "NotFound": {
        "description": "Not found",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      },
      "PaymentRequired": {
        "description": "Stripe checkout failed (e.g. invalid price, payment declined)",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      }
    }
  }
}