{
  "openapi": "3.1.0",
  "info": {
    "title": "Nabu API",
    "version": "v1",
    "description": "Manage brands and generate assets programmatically. Generated from the running API definitions."
  },
  "servers": [
    {
      "url": "https://nabu.ammoura.me"
    }
  ],
  "components": {
    "securitySchemes": {
      "bearerAuth": {
        "type": "http",
        "scheme": "bearer",
        "description": "An API key: nabu_sk_…"
      }
    },
    "schemas": {
      "Error": {
        "type": "object",
        "properties": {
          "error": {
            "type": "object",
            "properties": {
              "code": {
                "type": "string",
                "enum": [
                  "invalid_json",
                  "invalid_body",
                  "invalid_style",
                  "instruction_too_long",
                  "unsupported_model",
                  "missing_credentials",
                  "invalid_key",
                  "insufficient_scope",
                  "read_only_access",
                  "brand_not_found",
                  "asset_not_found",
                  "asset_content_missing",
                  "generation_failed",
                  "unavailable",
                  "ai_unavailable",
                  "storage_unavailable"
                ]
              },
              "message": {
                "type": "string"
              }
            }
          }
        }
      }
    }
  },
  "x-scopes": [
    "brands:read",
    "brands:write",
    "assets:read",
    "assets:write"
  ],
  "paths": {
    "/api/v1/brands": {
      "get": {
        "summary": "List brands",
        "description": "Brands this key can reach — owned, plus any shared with you — newest first, capped at 100. A brand-scoped key returns only its own.",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "x-required-scope": "brands:read",
        "responses": {
          "200": {
            "description": "`200` with `{ data: [ { id, name, tagline, status, industry, colors, logo_url, role, … } ] }`."
          },
          "401": {
            "description": "Missing or invalid API key."
          },
          "403": {
            "description": "Key lacks the required scope, or role cannot write."
          },
          "404": {
            "description": "Not found, or not reachable by this key."
          }
        }
      }
    },
    "/api/v1/brands/{id}/logos": {
      "get": {
        "summary": "List logos",
        "description": "Logo assets for the brand, newest first, plus which one is currently assigned as the brand's logo.",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "x-required-scope": "assets:read",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "`200` with `{ data: [ { id, name, url, mime_type, width, height, … } ], current_logo_url }`."
          },
          "401": {
            "description": "Missing or invalid API key."
          },
          "403": {
            "description": "Key lacks the required scope, or role cannot write."
          },
          "404": {
            "description": "Not found, or not reachable by this key."
          }
        }
      },
      "post": {
        "summary": "Generate a logo",
        "description": "Generates a logo and stores it as an asset. Runs synchronously, so one request returns a finished asset with no polling. The prompt is built from the brand's own name, industry, personality and palette, plus constraints that keep output usable as a mark rather than as illustration.",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "x-required-scope": "assets:write",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "`201` with `{ data: { id, generation_id, url, style, prompt, model, width, height, set_as_logo } }`."
          },
          "401": {
            "description": "Missing or invalid API key."
          },
          "403": {
            "description": "Key lacks the required scope, or role cannot write."
          },
          "404": {
            "description": "Not found, or not reachable by this key."
          }
        },
        "requestBody": {
          "required": false,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "style": {
                    "type": "string",
                    "description": "One of the styles listed above.",
                    "enum": [
                      "wordmark",
                      "lettermark",
                      "abstract",
                      "mascot",
                      "emblem"
                    ],
                    "default": "abstract"
                  },
                  "instruction": {
                    "type": "string",
                    "description": "Extra direction, up to 500 characters. Added to the constraints rather than replacing them, so output stays printable."
                  },
                  "set_as_logo": {
                    "type": "boolean",
                    "description": "Assign the result as the brand's logo. Defaults to false: generating and choosing are separate decisions, and an app usually wants to offer candidates before overwriting a mark already in use.",
                    "default": "false"
                  },
                  "model": {
                    "type": "string",
                    "description": "v1 accepts Workers AI image models only.",
                    "default": "Workers AI FLUX"
                  }
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/brands/{id}/assets/{assetId}/content": {
      "get": {
        "summary": "Fetch asset bytes",
        "description": "Streams the asset itself. Asset URLs from this API point here rather than at the app's own file route, which needs a browser session and would be a dead link to an API client. The lookup is scoped to the brand in the path, so an asset id belonging to another brand resolves to nothing.",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "x-required-scope": "assets:read",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "assetId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The raw file, with its `content-type`."
          },
          "401": {
            "description": "Missing or invalid API key."
          },
          "403": {
            "description": "Key lacks the required scope, or role cannot write."
          },
          "404": {
            "description": "Not found, or not reachable by this key."
          }
        }
      }
    }
  }
}