{
  "openapi": "3.1.0",
  "info": {
    "title": "Selah API",
    "version": "1.0.0",
    "description": "Selah is a brand deal pricing engine for content creators. Pass a brand's outreach message and get back an itemized quote, ready-to-paste bullet points, and a professional email draft — all priced to the creator's profile and currency.\n\n## Quickstart\n\n**1. Get an API key**\n\nLog in at [selah.fyi](https://www.selah.fyi), go to **Settings → API Access**, and generate a key. Copy it immediately — it's shown once.\n\n**2. Make your first quote**\n\n```bash\ncurl -X POST https://www.selah.fyi/api/quote \\\n  -H \"Authorization: Bearer sk_selah_YOUR_KEY\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\"message\": \"Hi! We'd love to work with you on a sponsored Instagram post for our skincare line.\"}'\n```\n\n**3. Read the response**\n\nYou'll get back itemized deliverables, a total in your currency, and `bullet_points` ready to paste into a reply.\n\n---\n\n## Authentication\n\nAll endpoints require a Bearer token in the `Authorization` header:\n\n```\nAuthorization: Bearer sk_selah_...\n```\n\nGenerate and manage keys at [selah.fyi/settings](https://www.selah.fyi/settings). Each key is tied to your account and shares your existing quota pool — there is no separate API quota.\n\n---\n\n## Rate Limits & Quota\n\nAI-powered endpoints (`POST /api/quote`, `POST /api/email-draft`) count against your weekly (Free) or daily (Pro) quota. Use `GET /api/quota` to check remaining usage before making calls.\n\nCompute-only endpoints like `POST /api/quote/compute` do **not** consume quota — they just recalculate math.\n\n---\n\n## MCP Setup\n\nSelah exposes a [Model Context Protocol](https://modelcontextprotocol.io) server at `/api/mcp`. Add it to any MCP-compatible agent client:\n\n```json\n{\n  \"mcpServers\": {\n    \"selah\": {\n      \"url\": \"https://www.selah.fyi/api/mcp\",\n      \"headers\": {\n        \"Authorization\": \"Bearer sk_selah_YOUR_KEY\"\n      }\n    }\n  }\n}\n```\n\nOr paste [selah.fyi/skills.md](https://www.selah.fyi/skills.md) into your agent's system prompt to give it Selah awareness without a client integration."
  },
  "servers": [
    { "url": "/" }
  ],
  "tags": [
    {
      "name": "Quotes",
      "description": "Price a brand deal from a raw outreach message, or recompute totals from edited deliverables without burning quota."
    },
    {
      "name": "Emails",
      "description": "Generate a professional reply email from a quote's bullet points."
    },
    {
      "name": "Deals",
      "description": "Read and update the creator's deal history."
    },
    {
      "name": "Quota",
      "description": "Check remaining AI usage quota for the current period (weekly for Free, daily for Pro)."
    },
    {
      "name": "MCP",
      "description": "Model Context Protocol server endpoint. Accepts JSON-RPC 2.0 messages from agent clients and returns tool results."
    }
  ],
  "components": {
    "securitySchemes": {
      "bearerAuth": {
        "type": "http",
        "scheme": "bearer",
        "description": "API key generated at selah.fyi/settings. Format: sk_selah_<32-hex-chars>"
      }
    },
    "schemas": {
      "LineItem": {
        "type": "object",
        "required": ["id", "deliverable_type", "label", "unit_label", "quantity", "rate", "subtotal"],
        "properties": {
          "id": { "type": "string" },
          "deliverable_type": { "type": "string" },
          "label": { "type": "string" },
          "unit_label": { "type": "string" },
          "quantity": { "type": "number" },
          "rate": { "type": "number" },
          "subtotal": { "type": "number" },
          "is_estimated": { "type": "boolean" }
        }
      },
      "AddonsConfig": {
        "type": "object",
        "description": "Add-on fees beyond base deliverables"
      },
      "QuoteResult": {
        "type": "object",
        "required": ["brand_name", "line_items", "addons", "total", "currency", "bullet_points", "quota"],
        "properties": {
          "brand_name": { "type": "string" },
          "line_items": { "type": "array", "items": { "$ref": "#/components/schemas/LineItem" } },
          "addons": { "$ref": "#/components/schemas/AddonsConfig" },
          "total": { "type": "number" },
          "currency": { "type": "string" },
          "bullet_points": { "type": "array", "items": { "type": "string" } },
          "quota": { "$ref": "#/components/schemas/QuotaStatus" },
          "detected_brand_region": { "type": "string", "nullable": true },
          "detected_deal_currency": { "type": "string", "nullable": true },
          "pricing_notes": { "type": "array", "items": { "type": "string" } },
          "auto_switched_currency": { "type": "boolean" },
          "at_deal_limit": { "type": "boolean" },
          "brand_initial_offer": { "type": "number", "nullable": true }
        }
      },
      "Deal": {
        "type": "object",
        "required": ["id", "user_id", "brand_name", "status", "total", "currency", "created_at"],
        "properties": {
          "id": { "type": "string", "format": "uuid" },
          "user_id": { "type": "string", "format": "uuid" },
          "brand_name": { "type": "string" },
          "status": { "type": "string", "enum": ["draft", "sent", "negotiation", "accepted", "rejected"] },
          "total": { "type": "number" },
          "currency": { "type": "string" },
          "deliverables": { "type": "array", "items": { "$ref": "#/components/schemas/LineItem" } },
          "addons": { "$ref": "#/components/schemas/AddonsConfig" },
          "sent_amount": { "type": "number", "nullable": true },
          "accepted_amount": { "type": "number", "nullable": true },
          "brand_initial_offer": { "type": "number", "nullable": true },
          "created_at": { "type": "string", "format": "date-time" }
        }
      },
      "QuotaStatus": {
        "type": "object",
        "required": ["allowed", "used", "limit", "remaining", "isPro", "tier"],
        "properties": {
          "allowed": { "type": "boolean" },
          "used": { "type": "number" },
          "limit": { "type": "number" },
          "remaining": { "type": "number" },
          "isPro": { "type": "boolean" },
          "isLastFree": { "type": "boolean" },
          "tier": { "type": "string", "enum": ["free", "pro", "founding"] },
          "dealLimit": { "type": "number" },
          "trialDaysLeft": { "type": "number", "nullable": true }
        }
      },
      "Error": {
        "type": "object",
        "required": ["error"],
        "properties": {
          "error": { "type": "string" }
        }
      }
    }
  },
  "security": [{ "bearerAuth": [] }],
  "paths": {
    "/api/quote": {
      "post": {
        "tags": ["Quotes"],
        "operationId": "createQuote",
        "summary": "Price a brand deal",
        "description": "Takes a raw brand outreach message and returns an itemized quote with deliverables, total price in the creator's currency, and bullet points ready to paste into a reply. AI-powered — consumes one quota unit.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": ["message"],
                "properties": {
                  "message": { "type": "string", "description": "The brand's outreach message, pasted verbatim" },
                  "brand_region": { "type": "string", "description": "Override detected brand region (e.g. 'US', 'EU')" },
                  "deal_niche": { "type": "array", "items": { "type": "string" }, "description": "Override detected niche(s)" },
                  "deal_currency": { "type": "string", "description": "Lock currency; omit to allow auto-detect" }
                }
              },
              "example": {
                "message": "Hi! We'd love to work with you on a sponsored Instagram post for our skincare line. Budget is $500."
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Quote result",
            "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuoteResult" } } }
          },
          "401": { "description": "Unauthorized", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } },
          "429": { "description": "Quota exceeded", "content": { "application/json": { "schema": { "allOf": [{ "$ref": "#/components/schemas/Error" }, { "properties": { "quota": { "$ref": "#/components/schemas/QuotaStatus" } } }] } } } }
        }
      }
    },
    "/api/quote/compute": {
      "post": {
        "tags": ["Quotes"],
        "operationId": "computeQuote",
        "summary": "Recompute totals from edited line items",
        "description": "Recalculates total and bullet points from edited deliverables and add-ons. No AI call, no quota consumed. Use this after a user edits quantities or rates from an initial quote.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": ["line_items", "addons"],
                "properties": {
                  "line_items": { "type": "array", "items": { "$ref": "#/components/schemas/LineItem" } },
                  "addons": { "$ref": "#/components/schemas/AddonsConfig" },
                  "currency": { "type": "string", "default": "USD" }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Recomputed totals",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "line_items": { "type": "array", "items": { "$ref": "#/components/schemas/LineItem" } },
                    "total": { "type": "number" },
                    "bullet_points": { "type": "array", "items": { "type": "string" } }
                  }
                }
              }
            }
          },
          "401": { "description": "Unauthorized", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }
        }
      }
    },
    "/api/email-draft": {
      "post": {
        "tags": ["Emails"],
        "operationId": "generateEmail",
        "summary": "Generate a reply email",
        "description": "Generates a professional email reply to a brand using a quote's bullet points. AI-powered — consumes one quota unit.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": ["scenario", "brand_name", "currency", "bullet_points"],
                "properties": {
                  "scenario": { "type": "string", "enum": ["initial_reply", "counter_reply"] },
                  "tone": { "type": "string", "enum": ["friendly", "professional"], "description": "Defaults to the creator's saved email tone." },
                  "brand_name": { "type": "string" },
                  "currency": { "type": "string" },
                  "bullet_points": { "type": "array", "items": { "type": "string" } },
                  "deal_id": { "type": "string", "format": "uuid", "nullable": true },
                  "original_message": { "type": "string", "nullable": true },
                  "brand_counter_offer": { "type": "number", "nullable": true }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Generated email",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "email": { "type": "string" },
                    "quota": { "$ref": "#/components/schemas/QuotaStatus" }
                  }
                }
              }
            }
          },
          "401": { "description": "Unauthorized", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } },
          "429": { "description": "Quota exceeded", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }
        }
      }
    },
    "/api/counter-offer": {
      "post": {
        "tags": ["Deals"],
        "operationId": "analyzeCounterOffer",
        "summary": "Read a brand's counter-offer",
        "description": "Extracts the amount a brand is offering from their pasted reply to a sent quote. The deal must be in `sent` or `negotiation` status. When an amount is found it is saved on the deal and the deal moves to `negotiation`. When the brand simply accepted her number, `brand_counter_offer` is null, `brand_accepted` is true and `suggested_close_amount` is returned; status is left alone. AI-powered — consumes one quota unit.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": ["deal_id", "email_text"],
                "properties": {
                  "deal_id": { "type": "string", "format": "uuid" },
                  "email_text": { "type": "string", "description": "The brand's reply, pasted verbatim." },
                  "currency": { "type": "string", "description": "Defaults to the deal's currency." }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Extracted offer",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "brand_counter_offer": { "type": "number", "nullable": true },
                    "brand_accepted": { "type": "boolean" },
                    "suggested_close_amount": { "type": "number" },
                    "quota": { "$ref": "#/components/schemas/QuotaStatus" }
                  }
                }
              }
            }
          },
          "400": { "description": "Missing fields, or the deal is not in sent or negotiation status", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } },
          "401": { "description": "Unauthorized", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } },
          "404": { "description": "Deal not found", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } },
          "422": { "description": "No offer amount could be read from the text", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } },
          "429": { "description": "Quota exceeded", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }
        }
      }
    },
    "/api/deals": {
      "get": {
        "tags": ["Deals"],
        "operationId": "listDeals",
        "summary": "List deals",
        "description": "Returns the creator's deal history, ordered by most recent. Filter by status or limit results.",
        "parameters": [
          { "name": "status", "in": "query", "schema": { "type": "string", "enum": ["draft", "sent", "negotiation", "accepted", "rejected"] }, "description": "Filter by deal status" },
          { "name": "limit", "in": "query", "schema": { "type": "integer", "default": 20, "maximum": 50 }, "description": "Number of results (max 50)" }
        ],
        "responses": {
          "200": {
            "description": "Deal list",
            "content": { "application/json": { "schema": { "type": "object", "properties": { "deals": { "type": "array", "items": { "$ref": "#/components/schemas/Deal" } } } } } }
          },
          "401": { "description": "Unauthorized", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }
        }
      }
    },
    "/api/deals/{id}": {
      "get": {
        "tags": ["Deals"],
        "operationId": "getDeal",
        "summary": "Get a deal",
        "description": "Returns full details of a single deal by ID.",
        "parameters": [{ "name": "id", "in": "path", "required": true, "schema": { "type": "string", "format": "uuid" } }],
        "responses": {
          "200": { "description": "Deal", "content": { "application/json": { "schema": { "type": "object", "properties": { "deal": { "$ref": "#/components/schemas/Deal" } } } } } },
          "401": { "description": "Unauthorized", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } },
          "404": { "description": "Not found", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }
        }
      },
      "patch": {
        "tags": ["Deals"],
        "operationId": "updateDeal",
        "summary": "Update deal status",
        "description": "Advances a deal through its lifecycle. Pass `sent_amount` when marking sent, `accepted_amount` when marking accepted.",
        "parameters": [{ "name": "id", "in": "path", "required": true, "schema": { "type": "string", "format": "uuid" } }],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "status": { "type": "string", "enum": ["sent", "negotiation", "accepted", "rejected"] },
                  "sent_amount": { "type": "number" },
                  "accepted_amount": { "type": "number" },
                  "rejection_reason": { "type": "string" }
                }
              }
            }
          }
        },
        "responses": {
          "200": { "description": "Updated deal", "content": { "application/json": { "schema": { "type": "object", "properties": { "deal": { "$ref": "#/components/schemas/Deal" } } } } } },
          "401": { "description": "Unauthorized", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } },
          "404": { "description": "Not found", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }
        }
      }
    },
    "/api/quota": {
      "get": {
        "tags": ["Quota"],
        "operationId": "getQuota",
        "summary": "Check quota",
        "description": "Returns quota usage for the current period. Free accounts reset weekly (Monday); Pro accounts reset daily.",
        "responses": {
          "200": { "description": "Quota status", "content": { "application/json": { "schema": { "type": "object", "properties": { "quota": { "$ref": "#/components/schemas/QuotaStatus" } } } } } },
          "401": { "description": "Unauthorized", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }
        }
      }
    },
    "/api/mcp": {
      "post": {
        "tags": ["MCP"],
        "operationId": "mcpServer",
        "summary": "MCP server endpoint",
        "description": "Model Context Protocol server. Accepts JSON-RPC 2.0 messages and returns tool results. Supports `initialize`, `tools/list`, and `tools/call` methods.\n\nAvailable tools: `selah_get_quote`, `selah_generate_email`, `selah_analyze_counter_offer`, `selah_list_deals`, `selah_get_deal`, `selah_update_deal_status`, `selah_get_quota`.\n\nAuth: Bearer API key only (no session cookies).",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": ["jsonrpc", "method"],
                "properties": {
                  "jsonrpc": { "type": "string", "enum": ["2.0"] },
                  "method": { "type": "string", "description": "MCP method: initialize, tools/list, tools/call" },
                  "params": { "type": "object" },
                  "id": { "oneOf": [{ "type": "number" }, { "type": "string" }] }
                }
              },
              "example": {
                "jsonrpc": "2.0",
                "method": "tools/list",
                "id": 1
              }
            }
          }
        },
        "responses": {
          "200": { "description": "JSON-RPC response" },
          "401": { "description": "Unauthorized", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }
        }
      }
    }
  }
}
