{
  "openapi": "3.1.0",
  "info": {
    "title": "AuraVMS API",
    "description": "RFQ software and vendor management API for small businesses. Create RFQs, manage suppliers, compare quotes, and place purchase orders.",
    "version": "1.1.0",
    "contact": {
      "email": "dhrumil@auravms.com"
    }
  },
  "servers": [
    {
      "url": "https://api.auravms.com",
      "description": "Production"
    },
    {
      "url": "http://localhost:8000",
      "description": "Local development"
    }
  ],
  "security": [
    {
      "BearerAuth": []
    }
  ],
  "components": {
    "securitySchemes": {
      "BearerAuth": {
        "type": "http",
        "scheme": "bearer",
        "description": "JWT token or API key (avms_ prefix). Get JWT via POST /auth/login/. Create API keys via POST /api/api-keys/."
      }
    },
    "schemas": {
      "SuccessResponse": {
        "type": "object",
        "properties": {
          "success": {
            "type": "boolean",
            "example": true
          }
        }
      },
      "ErrorResponse": {
        "type": "object",
        "properties": {
          "success": {
            "type": "boolean",
            "example": false
          },
          "error": {
            "type": "string"
          }
        }
      }
    }
  },
  "paths": {
    "/auth/login/": {
      "post": {
        "tags": [
          "Authentication"
        ],
        "summary": "Login and get JWT tokens",
        "security": [],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "email",
                  "password"
                ],
                "properties": {
                  "email": {
                    "type": "string",
                    "example": "buyer@company.com"
                  },
                  "password": {
                    "type": "string"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "JWT tokens",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "access": {
                      "type": "string"
                    },
                    "refresh": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/api/api-keys/": {
      "get": {
        "tags": [
          "API Keys"
        ],
        "summary": "List all API keys",
        "responses": {
          "200": {
            "description": "List of API keys"
          }
        }
      },
      "post": {
        "tags": [
          "API Keys"
        ],
        "summary": "Create a new API key",
        "description": "Returns the raw key ONCE. Save it immediately.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "name"
                ],
                "properties": {
                  "name": {
                    "type": "string",
                    "example": "My Integration"
                  },
                  "scopes": {
                    "type": "string",
                    "example": "rfq,suppliers,orders,dashboard,export"
                  },
                  "expires_in_days": {
                    "type": "integer",
                    "example": 365
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "API key created",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean"
                    },
                    "key": {
                      "type": "string",
                      "description": "Raw key - save immediately"
                    },
                    "id": {
                      "type": "integer"
                    },
                    "name": {
                      "type": "string"
                    },
                    "key_prefix": {
                      "type": "string"
                    },
                    "scopes": {
                      "type": "array",
                      "items": {
                        "type": "string"
                      }
                    }
                  }
                }
              }
            }
          },
          "409": {
            "description": "Idempotency key is processing or was already used with a different request body"
          }
        },
        "parameters": [
          {
            "name": "Idempotency-Key",
            "in": "header",
            "required": false,
            "description": "Replay key for JSON mutations. Reusing a key with the same body returns the original response for 24 hours; reusing it with a different body returns 409.",
            "schema": {
              "type": "string",
              "minLength": 8,
              "maxLength": 255,
              "pattern": "^[A-Za-z0-9._:-]+$"
            }
          }
        ]
      }
    },
    "/api/api-keys/{key_id}/revoke/": {
      "post": {
        "tags": [
          "API Keys"
        ],
        "summary": "Revoke an API key",
        "parameters": [
          {
            "name": "key_id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer"
            }
          },
          {
            "name": "Idempotency-Key",
            "in": "header",
            "required": false,
            "description": "Replay key for JSON mutations. Reusing a key with the same body returns the original response for 24 hours; reusing it with a different body returns 409.",
            "schema": {
              "type": "string",
              "minLength": 8,
              "maxLength": 255,
              "pattern": "^[A-Za-z0-9._:-]+$"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Key revoked"
          },
          "409": {
            "description": "Idempotency key is processing or was already used with a different request body"
          }
        }
      }
    },
    "/api/create-rfq/": {
      "post": {
        "tags": [
          "RFQ Management"
        ],
        "summary": "Create a new RFQ and notify suppliers",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "title",
                  "items"
                ],
                "properties": {
                  "title": {
                    "type": "string",
                    "example": "Q3 Raw Materials"
                  },
                  "terms_and_condition": {
                    "type": "string"
                  },
                  "payment_terms": {
                    "type": "string"
                  },
                  "shipping_terms": {
                    "type": "string"
                  },
                  "items": {
                    "type": "array",
                    "items": {
                      "type": "object",
                      "required": [
                        "product_name",
                        "quantity"
                      ],
                      "properties": {
                        "product_name": {
                          "type": "string"
                        },
                        "quantity": {
                          "type": "number"
                        },
                        "uom": {
                          "type": "string"
                        },
                        "specifications": {
                          "type": "string"
                        },
                        "expected_delivery_date": {
                          "type": "string",
                          "format": "date"
                        }
                      }
                    }
                  },
                  "suppliers": {
                    "type": "array",
                    "items": {
                      "type": "string",
                      "format": "uuid"
                    }
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "RFQ created",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean"
                    },
                    "rfq_id": {
                      "type": "integer"
                    },
                    "created_items": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "id": {
                            "type": "integer"
                          },
                          "index": {
                            "type": "integer"
                          },
                          "product_name": {
                            "type": "string"
                          }
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "409": {
            "description": "Idempotency key is processing or was already used with a different request body"
          }
        },
        "parameters": [
          {
            "name": "Idempotency-Key",
            "in": "header",
            "required": false,
            "description": "Replay key for JSON mutations. Reusing a key with the same body returns the original response for 24 hours; reusing it with a different body returns 409.",
            "schema": {
              "type": "string",
              "minLength": 8,
              "maxLength": 255,
              "pattern": "^[A-Za-z0-9._:-]+$"
            }
          }
        ]
      }
    },
    "/api/get-rfq-list/": {
      "get": {
        "tags": [
          "RFQ Management"
        ],
        "summary": "List all RFQs",
        "parameters": [
          {
            "name": "page",
            "in": "query",
            "schema": {
              "type": "integer"
            }
          },
          {
            "name": "page_size",
            "in": "query",
            "schema": {
              "type": "integer"
            }
          },
          {
            "name": "status",
            "in": "query",
            "schema": {
              "type": "string",
              "enum": [
                "open",
                "closed"
              ]
            }
          },
          {
            "name": "search",
            "in": "query",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Paginated RFQ list"
          }
        }
      }
    },
    "/api/get-rfq-items/{rfq_id}/": {
      "get": {
        "tags": [
          "RFQ Management"
        ],
        "summary": "Get RFQ details with all line items",
        "parameters": [
          {
            "name": "rfq_id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "RFQ details with items and supplier status"
          }
        }
      }
    },
    "/api/rfq-item-data/{rfq_item_id}": {
      "get": {
        "tags": [
          "RFQ Management"
        ],
        "summary": "Get item with all supplier responses and L1/L2/L3 ranking",
        "parameters": [
          {
            "name": "rfq_item_id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Item with ranked supplier quotes"
          }
        }
      },
      "post": {
        "tags": [
          "Order Placement"
        ],
        "summary": "Place a purchase order against a supplier quote",
        "parameters": [
          {
            "name": "rfq_item_id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer"
            }
          },
          {
            "name": "Idempotency-Key",
            "in": "header",
            "required": false,
            "description": "Replay key for JSON mutations. Reusing a key with the same body returns the original response for 24 hours; reusing it with a different body returns 409.",
            "schema": {
              "type": "string",
              "minLength": 8,
              "maxLength": 255,
              "pattern": "^[A-Za-z0-9._:-]+$"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "response_id"
                ],
                "properties": {
                  "response_id": {
                    "type": "integer"
                  },
                  "bought_quantity": {
                    "type": "number"
                  },
                  "bought_price": {
                    "type": "number"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Order placed, supplier notified"
          },
          "409": {
            "description": "Idempotency key is processing or was already used with a different request body"
          }
        }
      }
    },
    "/api/close-rfq/{rfq_id}/": {
      "post": {
        "tags": [
          "RFQ Management"
        ],
        "summary": "Close an RFQ",
        "parameters": [
          {
            "name": "rfq_id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer"
            }
          },
          {
            "name": "Idempotency-Key",
            "in": "header",
            "required": false,
            "description": "Replay key for JSON mutations. Reusing a key with the same body returns the original response for 24 hours; reusing it with a different body returns 409.",
            "schema": {
              "type": "string",
              "minLength": 8,
              "maxLength": 255,
              "pattern": "^[A-Za-z0-9._:-]+$"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "RFQ closed"
          },
          "409": {
            "description": "Idempotency key is processing or was already used with a different request body"
          }
        }
      }
    },
    "/api/create-supplier/": {
      "post": {
        "tags": [
          "Supplier Management"
        ],
        "summary": "Add a new supplier",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "company_name",
                  "person_of_contact",
                  "phone_no",
                  "email"
                ],
                "properties": {
                  "company_name": {
                    "type": "string"
                  },
                  "person_of_contact": {
                    "type": "string"
                  },
                  "phone_no": {
                    "type": "string"
                  },
                  "email": {
                    "type": "string",
                    "format": "email"
                  },
                  "categories": {
                    "type": "array",
                    "items": {
                      "type": "string"
                    }
                  },
                  "remark": {
                    "type": "string"
                  },
                  "location": {
                    "type": "string",
                    "maxLength": 120,
                    "description": "Optional city, e.g. \"Pune\". Matched case-insensitively against existing supplier locations and reuses their spelling."
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Supplier created; returns supplier_id"
          },
          "409": {
            "description": "Idempotency key is processing or was already used with a different request body"
          }
        },
        "parameters": [
          {
            "name": "Idempotency-Key",
            "in": "header",
            "required": false,
            "description": "Replay key for JSON mutations. Reusing a key with the same body returns the original response for 24 hours; reusing it with a different body returns 409.",
            "schema": {
              "type": "string",
              "minLength": 8,
              "maxLength": 255,
              "pattern": "^[A-Za-z0-9._:-]+$"
            }
          }
        ]
      },
      "put": {
        "tags": [
          "Supplier Management"
        ],
        "summary": "Update a supplier",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "supplier_id",
                  "fields_updated"
                ],
                "properties": {
                  "supplier_id": {
                    "type": "string",
                    "format": "uuid"
                  },
                  "fields_updated": {
                    "type": "array",
                    "items": {
                      "type": "string",
                      "enum": [
                        "company_name",
                        "person_of_contact",
                        "phone_no",
                        "email",
                        "categories",
                        "remark",
                        "location"
                      ]
                    }
                  },
                  "company_name": {
                    "type": "string"
                  },
                  "person_of_contact": {
                    "type": "string"
                  },
                  "phone_no": {
                    "type": "string"
                  },
                  "email": {
                    "type": "string",
                    "format": "email"
                  },
                  "categories": {
                    "type": "array",
                    "items": {
                      "type": "string"
                    }
                  },
                  "remark": {
                    "type": "string"
                  },
                  "location": {
                    "type": [
                      "string",
                      "null"
                    ],
                    "maxLength": 120,
                    "description": "Include \"location\" in fields_updated. An empty string or null clears it."
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Supplier updated"
          },
          "409": {
            "description": "Idempotency key is processing or was already used with a different request body"
          }
        },
        "parameters": [
          {
            "name": "Idempotency-Key",
            "in": "header",
            "required": false,
            "description": "Replay key for JSON mutations. Reusing a key with the same body returns the original response for 24 hours; reusing it with a different body returns 409.",
            "schema": {
              "type": "string",
              "minLength": 8,
              "maxLength": 255,
              "pattern": "^[A-Za-z0-9._:-]+$"
            }
          }
        ]
      }
    },
    "/api/get-suppliers/": {
      "get": {
        "tags": [
          "Supplier Management"
        ],
        "summary": "List all suppliers",
        "parameters": [
          {
            "name": "search",
            "in": "query",
            "schema": {
              "type": "string"
            },
            "description": "Matches company, contact, phone, email, location or category. Alias: q."
          },
          {
            "name": "category",
            "in": "query",
            "schema": {
              "type": "string"
            },
            "description": "Exact category name (case-insensitive). Comma-separate for any-of."
          },
          {
            "name": "location",
            "in": "query",
            "schema": {
              "type": "string"
            },
            "description": "Exact location (case-insensitive). Comma-separate for any-of, e.g. Pune,Mumbai."
          }
        ],
        "responses": {
          "200": {
            "description": "Supplier list. Each supplier has supplier_id, company, person, phone, email, categories, remark and location (null when unset)."
          }
        }
      }
    },
    "/api/import-suppliers/": {
      "post": {
        "tags": [
          "Supplier Management"
        ],
        "summary": "Bulk import suppliers from a CSV or Excel file",
        "requestBody": {
          "required": true,
          "content": {
            "multipart/form-data": {
              "schema": {
                "type": "object",
                "required": [
                  "file"
                ],
                "properties": {
                  "file": {
                    "type": "string",
                    "format": "binary",
                    "description": "Columns: Company Name, Person Of Contact, Phone No, Email (required); Remark, Location (optional)."
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Import results"
          },
          "409": {
            "description": "Idempotency key is processing or was already used with a different request body"
          }
        },
        "parameters": [
          {
            "name": "Idempotency-Key",
            "in": "header",
            "required": false,
            "description": "Replay key for JSON mutations. Reusing a key with the same body returns the original response for 24 hours; reusing it with a different body returns 409.",
            "schema": {
              "type": "string",
              "minLength": 8,
              "maxLength": 255,
              "pattern": "^[A-Za-z0-9._:-]+$"
            }
          }
        ]
      }
    },
    "/api/send-reminders/": {
      "post": {
        "tags": [
          "RFQ Management"
        ],
        "summary": "Send reminders to non-responsive suppliers",
        "description": "Send reminders immediately, or include Prefer: respond-async to receive a pollable job.",
        "parameters": [
          {
            "name": "Prefer",
            "in": "header",
            "required": false,
            "schema": {
              "type": "string",
              "enum": [
                "respond-async"
              ]
            }
          },
          {
            "name": "Idempotency-Key",
            "in": "header",
            "required": false,
            "description": "Replay key for JSON mutations. Reusing a key with the same body returns the original response for 24 hours; reusing it with a different body returns 409.",
            "schema": {
              "type": "string",
              "minLength": 8,
              "maxLength": 255,
              "pattern": "^[A-Za-z0-9._:-]+$"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "rfq_item_id"
                ],
                "properties": {
                  "rfq_item_id": {
                    "type": "integer"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Reminders sent or queued using the default response contract"
          },
          "202": {
            "description": "Reminder job accepted",
            "headers": {
              "Location": {
                "description": "Absolute URL for polling the job.",
                "schema": {
                  "type": "string",
                  "format": "uri"
                }
              },
              "Preference-Applied": {
                "description": "Confirms asynchronous handling.",
                "schema": {
                  "type": "string",
                  "const": "respond-async"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "success",
                    "job_id",
                    "status",
                    "status_url"
                  ],
                  "properties": {
                    "success": {
                      "type": "boolean",
                      "const": true
                    },
                    "job_id": {
                      "type": "string"
                    },
                    "status": {
                      "type": "string",
                      "const": "queued"
                    },
                    "status_url": {
                      "type": "string",
                      "format": "uri"
                    }
                  }
                }
              }
            }
          },
          "409": {
            "description": "Idempotency key is processing or was already used with a different request body"
          }
        }
      }
    },
    "/api/async-jobs/{job_id}/": {
      "get": {
        "tags": [
          "RFQ Management"
        ],
        "summary": "Poll an asynchronous AuraVMS job",
        "parameters": [
          {
            "name": "job_id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Current job state",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "success",
                    "job_id",
                    "status",
                    "complete"
                  ],
                  "properties": {
                    "success": {
                      "type": "boolean",
                      "const": true
                    },
                    "job_id": {
                      "type": "string"
                    },
                    "status": {
                      "type": "string",
                      "enum": [
                        "pending",
                        "received",
                        "started",
                        "retry",
                        "success",
                        "failure",
                        "revoked"
                      ]
                    },
                    "complete": {
                      "type": "boolean"
                    },
                    "result": {}
                  }
                }
              }
            }
          },
          "404": {
            "description": "Job not found or not owned by the caller"
          }
        }
      }
    },
    "/api/dashboard-stats/": {
      "get": {
        "tags": [
          "Analytics"
        ],
        "summary": "Get dashboard statistics",
        "responses": {
          "200": {
            "description": "Dashboard stats (total RFQs, suppliers, responses, orders)"
          }
        }
      }
    },
    "/api/get-supplier-stats-data/": {
      "get": {
        "tags": [
          "Analytics"
        ],
        "summary": "Get supplier performance data",
        "responses": {
          "200": {
            "description": "Supplier stats (response rates, avg price, orders)"
          }
        }
      }
    },
    "/api/export-quotation-pdf/{rfq_item_id}/": {
      "get": {
        "tags": [
          "Data Export"
        ],
        "summary": "Export quote comparison as PDF",
        "parameters": [
          {
            "name": "rfq_item_id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "PDF file",
            "content": {
              "application/pdf": {}
            }
          }
        }
      }
    },
    "/api/get-rfq-metadata/": {
      "get": {
        "tags": [
          "RFQ Management"
        ],
        "summary": "Get RFQ metadata (terms, payment, shipping)",
        "parameters": [
          {
            "name": "rfq_id",
            "in": "query",
            "required": true,
            "schema": {
              "type": "integer"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "RFQ metadata"
          }
        }
      }
    },
    "/api/get-supplier-categories/": {
      "get": {
        "tags": [
          "Supplier Management"
        ],
        "summary": "Get all supplier categories",
        "responses": {
          "200": {
            "description": "Category list"
          }
        }
      }
    },
    "/api/get-supplier-locations/": {
      "get": {
        "tags": [
          "Supplier Management"
        ],
        "summary": "Get distinct supplier locations with supplier counts",
        "responses": {
          "200": {
            "description": "Location list: [{label, value, count}]"
          }
        }
      }
    },
    "/api/get-item-uom/": {
      "get": {
        "tags": [
          "Reference Data"
        ],
        "summary": "Get available units of measurement",
        "responses": {
          "200": {
            "description": "UOM list"
          }
        }
      }
    },
    "/api/create-rfq-response/": {
      "post": {
        "tags": [
          "Supplier Responses"
        ],
        "summary": "Submit supplier quotation (no auth required)",
        "security": [],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "rfq_id",
                  "supplier_id",
                  "items"
                ],
                "properties": {
                  "rfq_id": {
                    "type": "integer"
                  },
                  "supplier_id": {
                    "type": "string",
                    "format": "uuid"
                  },
                  "items": {
                    "type": "array",
                    "items": {
                      "type": "object",
                      "required": [
                        "rfq_item_id",
                        "quantity",
                        "price"
                      ],
                      "properties": {
                        "rfq_item_id": {
                          "type": "integer"
                        },
                        "quantity": {
                          "type": "number"
                        },
                        "price": {
                          "type": "number"
                        },
                        "supplier_lead_time": {
                          "type": "string"
                        },
                        "supplier_remarks": {
                          "type": "string"
                        }
                      }
                    }
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Response submitted"
          },
          "409": {
            "description": "Idempotency key is processing or was already used with a different request body"
          }
        },
        "parameters": [
          {
            "name": "Idempotency-Key",
            "in": "header",
            "required": false,
            "description": "Replay key for JSON mutations. Reusing a key with the same body returns the original response for 24 hours; reusing it with a different body returns 409.",
            "schema": {
              "type": "string",
              "minLength": 8,
              "maxLength": 255,
              "pattern": "^[A-Za-z0-9._:-]+$"
            }
          }
        ]
      }
    }
  }
}