{
  "openapi": "3.1.0",
  "info": {
    "title": "Cobuntu API",
    "version": "1.0.0",
    "description": "Public REST API for Cobuntu communities. Every endpoint is scope-gated by an API key you generate per-community in cobuntu-admin \u2192 Integrations \u2192 API Keys.",
    "contact": {
      "name": "Cobuntu",
      "email": "hello@cobuntu.com",
      "url": "https://docs.cobuntu.com"
    },
    "license": {
      "name": "Proprietary"
    }
  },
  "servers": [
    {
      "url": "https://api.cobuntu.com/api/v1",
      "description": "Production"
    }
  ],
  "tags": [
    {
      "name": "Events",
      "description": "Event listings + RSVP + tier forms."
    },
    {
      "name": "Articles",
      "description": "Published articles."
    },
    {
      "name": "Products",
      "description": "Marketplace products."
    },
    {
      "name": "Members",
      "description": "Public members directory + admin actions."
    },
    {
      "name": "Atlas",
      "description": "Community hotspots + categories."
    },
    {
      "name": "Segments",
      "description": "Member tiers / segments + their forms."
    },
    {
      "name": "Applications",
      "description": "Pending membership request review."
    },
    {
      "name": "Broadcasts",
      "description": "One-shot email sends."
    },
    {
      "name": "Rules",
      "description": "Community rules acceptance."
    }
  ],
  "security": [
    {
      "PublishableKey": []
    },
    {
      "SecretKey": []
    }
  ],
  "components": {
    "securitySchemes": {
      "PublishableKey": {
        "type": "apiKey",
        "in": "header",
        "name": "X-API-Key",
        "description": "Per-community publishable key (pk_live_\u2026). Browser-safe. Scope is capped at READ_PUBLIC. Auto-provisioned via GET /communities/{tag}/publishable-key (no auth)."
      },
      "SecretKey": {
        "type": "apiKey",
        "in": "header",
        "name": "X-API-Key",
        "description": "Per-community secret key (sk_live_\u2026). Server-only \u2014 never ship in browser bundles. Can carry any scope set."
      }
    },
    "schemas": {
      "Error": {
        "type": "object",
        "required": [
          "error"
        ],
        "properties": {
          "error": {
            "type": "string",
            "description": "Human-readable error message."
          },
          "code": {
            "type": "string",
            "description": "Machine-readable code (when set)."
          },
          "requestId": {
            "type": "string",
            "description": "Trace id for support correlation."
          }
        }
      },
      "Pagination": {
        "type": "object",
        "properties": {
          "limit": {
            "type": "integer",
            "default": 20,
            "maximum": 100,
            "description": "Number of items per page. Max 100."
          },
          "offset": {
            "type": "integer",
            "default": 0,
            "description": "Zero-based offset (when offset-paginated)."
          },
          "total": {
            "type": "integer",
            "description": "Total matching items across all pages."
          },
          "hasMore": {
            "type": "boolean",
            "description": "True if more pages exist past this one."
          }
        }
      },
      "Event": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "description": "Stable event identifier (uuid)."
          },
          "slug": {
            "type": "string",
            "description": "URL-safe identifier, unique per community. Use this for deep-links."
          },
          "name": {
            "type": "string",
            "description": "Display name shown on listings and detail pages."
          },
          "description": {
            "type": "string",
            "nullable": true,
            "description": "Long-form description (may contain markdown)."
          },
          "startDate": {
            "type": "string",
            "format": "date-time",
            "description": "ISO 8601 start timestamp. UTC. Convert to your visitor's locale client-side."
          },
          "endDate": {
            "type": "string",
            "format": "date-time",
            "nullable": true,
            "description": "ISO 8601 end timestamp. Null for open-ended events."
          },
          "location": {
            "type": "object",
            "nullable": true,
            "description": "Venue object (address / virtual link). Null for TBD."
          },
          "bannerUrl": {
            "type": "string",
            "nullable": true,
            "description": "Hero image URL (or null). Recommended display: 16:9."
          },
          "format": {
            "type": "string",
            "enum": [
              "online",
              "in_person",
              "hybrid"
            ],
            "description": "How the event is delivered. Drives icon + booking flow."
          },
          "tiers": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Tier"
            },
            "description": "Active price tiers for this event. Empty when the event isn't yet on sale."
          },
          "featured": {
            "type": "boolean",
            "description": "True if the event is the community's 'big frame' featured event. Only one event can be featured per community at a time."
          }
        }
      },
      "Tier": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "description": "Stable tier identifier (uuid)."
          },
          "name": {
            "type": "string",
            "description": "Display name (e.g. `Early bird`, `VIP`)."
          },
          "priceCents": {
            "type": "integer",
            "description": "Price in the smallest currency unit (cents for EUR/USD/GBP, no decimals for JPY)."
          },
          "currency": {
            "type": "string",
            "example": "EUR",
            "description": "ISO 4217 code."
          },
          "capacity": {
            "type": "integer",
            "nullable": true,
            "description": "Max attendees who can buy this tier. Null = unlimited."
          },
          "sold": {
            "type": "integer",
            "description": "Attendees already on this tier. Read-only."
          },
          "isActive": {
            "type": "boolean",
            "description": "Whether the tier accepts new RSVPs."
          },
          "hasForm": {
            "type": "boolean",
            "description": "True if this tier requires answering a form at checkout (use `GET /tiers/{tierId}/form` to fetch the schema)."
          }
        }
      },
      "Article": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "description": "Stable article identifier (uuid)."
          },
          "slug": {
            "type": "string",
            "description": "URL-safe identifier; stable across edits."
          },
          "title": {
            "type": "string",
            "description": "Article title."
          },
          "excerpt": {
            "type": "string",
            "description": "Short summary shown on list pages."
          },
          "publishedAt": {
            "type": "string",
            "format": "date-time",
            "description": "ISO 8601 publish timestamp. Articles only appear in this API once published."
          },
          "bannerUrl": {
            "type": "string",
            "nullable": true,
            "description": "Hero image URL (or null)."
          },
          "author": {
            "type": "object",
            "description": "Author object (`{ name, usertag, profileImage }`)."
          },
          "readTimeMinutes": {
            "type": "integer",
            "description": "Estimated reading time in minutes."
          },
          "content": {
            "type": "string",
            "description": "HTML body."
          }
        }
      },
      "Product": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "description": "Stable product identifier (uuid)."
          },
          "sku": {
            "type": "string",
            "description": "Stable per-community SKU; use this for deep-links and inventory keys."
          },
          "name": {
            "type": "string",
            "description": "Display name on product cards + detail pages."
          },
          "description": {
            "type": "string",
            "nullable": true,
            "description": "Long-form description (markdown)."
          },
          "priceCents": {
            "type": "integer",
            "description": "Price in the smallest currency unit."
          },
          "currency": {
            "type": "string",
            "description": "ISO 4217 code."
          },
          "imageUrl": {
            "type": "string",
            "nullable": true,
            "description": "Primary image (or null)."
          },
          "isActive": {
            "type": "boolean",
            "description": "Whether the product accepts new orders."
          },
          "sellerId": {
            "type": "string",
            "description": "User id of the member who listed the product (when the community has user-seller stack enabled)."
          }
        }
      },
      "Member": {
        "type": "object",
        "properties": {
          "userId": {
            "type": "string",
            "description": "Stable user identifier; use this in admin actions (`/members/{userId}/kick` etc)."
          },
          "name": {
            "type": "string",
            "description": "Display name."
          },
          "usertag": {
            "type": "string",
            "description": "Per-platform handle, unique across all of Cobuntu."
          },
          "profileImage": {
            "type": "string",
            "nullable": true,
            "description": "Avatar URL (or null)."
          },
          "bio": {
            "type": "string",
            "nullable": true,
            "description": "Self-written short bio (or null)."
          },
          "joinedAt": {
            "type": "string",
            "format": "date-time",
            "description": "ISO 8601 timestamp of when this user's membership was ACCEPTED."
          }
        }
      },
      "AtlasHotspot": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "description": "Stable hotspot identifier."
          },
          "name": {
            "type": "string",
            "description": "Display name."
          },
          "description": {
            "type": "string",
            "description": "Description shown in the hotspot card / detail."
          },
          "latitude": {
            "type": "number",
            "description": "Geographic latitude, decimal degrees, WGS-84."
          },
          "longitude": {
            "type": "number",
            "description": "Geographic longitude, decimal degrees, WGS-84."
          },
          "categoryId": {
            "type": "string",
            "description": "Foreign key to AtlasCategory."
          },
          "imageUrl": {
            "type": "string",
            "nullable": true,
            "description": "Photo URL (or null)."
          }
        }
      },
      "AtlasCategory": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "description": "Stable category identifier."
          },
          "name": {
            "type": "string",
            "description": "Display name (e.g. `Restaurant`, `Coworking`)."
          },
          "icon": {
            "type": "string",
            "nullable": true,
            "description": "Icon key from Cobuntu's icon library (or null)."
          },
          "color": {
            "type": "string",
            "nullable": true,
            "description": "Display color, hex (or null)."
          }
        }
      },
      "Segment": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "description": "Stable segment identifier (uuid)."
          },
          "name": {
            "type": "string",
            "description": "Display name (e.g. `Free`, `Pro`, `Founding Member`)."
          },
          "description": {
            "type": "string",
            "nullable": true,
            "description": "Long-form description shown on the segment's apply page."
          },
          "color": {
            "type": "string",
            "description": "Display color, hex."
          },
          "hasForm": {
            "type": "boolean",
            "description": "True if the segment requires submitting a form to join (use `GET /segments/{segmentId}/form`)."
          },
          "priceMonthly": {
            "type": "integer",
            "nullable": true,
            "description": "Monthly price in cents (null = not offered monthly)."
          },
          "priceQuarterly": {
            "type": "integer",
            "nullable": true,
            "description": "Quarterly price in cents (null = not offered quarterly)."
          },
          "priceYearly": {
            "type": "integer",
            "nullable": true,
            "description": "Yearly price in cents (null = not offered yearly)."
          }
        }
      },
      "Membership": {
        "type": "object",
        "properties": {
          "communityTag": {
            "type": "string",
            "description": "The community's tag \u2014 useful when one user belongs to multiple communities."
          },
          "status": {
            "type": "string",
            "enum": [
              "PENDING",
              "ACCEPTED",
              "REJECTED",
              "BANNED"
            ],
            "description": "Current membership status. `ACCEPTED` is the only state with full member privileges."
          },
          "platforms": {
            "type": "object",
            "properties": {
              "cobuntu": {
                "type": "object",
                "properties": {
                  "status": {
                    "type": "string"
                  }
                }
              }
            },
            "description": "Platform-specific status detail. Today only `cobuntu` is populated; reserved for future Slack/Discord/etc. integrations."
          },
          "createdAt": {
            "type": "string",
            "format": "date-time",
            "description": "ISO 8601 timestamp of when this row was created (request submission, not approval)."
          }
        }
      }
    },
    "responses": {
      "Unauthorized": {
        "description": "Missing or invalid API key.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      },
      "Forbidden": {
        "description": "API key lacks required scope.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      },
      "NotFound": {
        "description": "Resource not found.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      },
      "BadRequest": {
        "description": "Invalid request (validation failed).",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      },
      "UpstreamFailure": {
        "description": "Upstream service failure.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      },
      "RateLimited": {
        "description": "Rate limit exceeded.",
        "headers": {
          "Retry-After": {
            "schema": {
              "type": "integer"
            },
            "description": "Seconds to wait."
          }
        },
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      }
    }
  },
  "paths": {
    "/communities/{communityTag}/events": {
      "get": {
        "summary": "List events",
        "security": [
          {
            "PublishableKey": [
              "READ_PUBLIC"
            ]
          }
        ],
        "parameters": [
          {
            "name": "communityTag",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Your community's tag (e.g. `bela-escala`, `orbis`)."
          },
          {
            "name": "limit",
            "in": "query",
            "schema": {
              "type": "integer",
              "default": 20,
              "maximum": 100
            },
            "description": "Page size. Default 20, max 100."
          },
          {
            "name": "offset",
            "in": "query",
            "schema": {
              "type": "integer",
              "default": 0
            },
            "description": "Zero-based offset for pagination. Use either offset or cursor \u2014 not both."
          },
          {
            "name": "featured",
            "in": "query",
            "schema": {
              "type": "boolean"
            },
            "description": "If true, filter to the single featured event for this community."
          },
          {
            "name": "upcoming",
            "in": "query",
            "schema": {
              "type": "boolean"
            },
            "description": "If true, filter to events with `startDate` >= now."
          },
          {
            "name": "cursor",
            "in": "query",
            "schema": {
              "type": "string"
            },
            "description": "Opaque cursor from a previous response's `pagination.nextCursor`. Use instead of offset for cursor-paginated endpoints."
          }
        ],
        "responses": {
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "502": {
            "$ref": "#/components/responses/UpstreamFailure"
          },
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/Event"
                  }
                }
              }
            }
          }
        }
      }
    },
    "/communities/{communityTag}/events/{slug}": {
      "get": {
        "summary": "Get event by slug",
        "security": [
          {
            "PublishableKey": [
              "READ_PUBLIC"
            ]
          }
        ],
        "parameters": [
          {
            "name": "communityTag",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Your community's tag (e.g. `bela-escala`, `orbis`)."
          },
          {
            "name": "slug",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "URL slug from the event / article. Stable across edits."
          }
        ],
        "responses": {
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "502": {
            "$ref": "#/components/responses/UpstreamFailure"
          },
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Event"
                }
              }
            }
          }
        }
      }
    },
    "/communities/{communityTag}/events/{slug}/tiers": {
      "get": {
        "summary": "List tiers for an event",
        "security": [
          {
            "PublishableKey": [
              "READ_PUBLIC"
            ]
          }
        ],
        "parameters": [
          {
            "name": "communityTag",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Your community's tag (e.g. `bela-escala`, `orbis`)."
          },
          {
            "name": "slug",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "URL slug from the event / article. Stable across edits."
          }
        ],
        "responses": {
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "502": {
            "$ref": "#/components/responses/UpstreamFailure"
          },
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/Tier"
                  }
                }
              }
            }
          }
        }
      }
    },
    "/communities/{communityTag}/events/{slug}/rsvp": {
      "post": {
        "summary": "RSVP to a free event",
        "security": [
          {
            "SecretKey": [
              "WRITE_SALES"
            ]
          }
        ],
        "parameters": [
          {
            "name": "communityTag",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Your community's tag (e.g. `bela-escala`, `orbis`)."
          },
          {
            "name": "slug",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "URL slug from the event / article. Stable across edits."
          }
        ],
        "responses": {
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "502": {
            "$ref": "#/components/responses/UpstreamFailure"
          },
          "200": {
            "description": "RSVP created",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "ok": {
                      "type": "boolean",
                      "description": "Always `true` on a 200 response."
                    },
                    "rsvpId": {
                      "type": "string",
                      "description": "Stable id for the new RSVP."
                    },
                    "tierId": {
                      "type": "string",
                      "description": "Mirrors the requested `tierId`."
                    },
                    "qrCode": {
                      "type": "string",
                      "description": "Base64 QR code for venue check-in (encoded the email also receives)."
                    }
                  }
                }
              }
            }
          }
        },
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "tierId",
                  "email",
                  "name"
                ],
                "properties": {
                  "tierId": {
                    "type": "string",
                    "description": "Tier the RSVP attaches to. Must be active + free (priceCents = 0). For paid tiers, use Checkout instead."
                  },
                  "email": {
                    "type": "string",
                    "format": "email",
                    "description": "Attendee email. Used for confirmation + reminder emails."
                  },
                  "name": {
                    "type": "string",
                    "description": "Attendee display name."
                  },
                  "formAnswers": {
                    "type": "object",
                    "description": "Tier form answers if the tier has a form."
                  }
                }
              }
            }
          }
        }
      }
    },
    "/communities/{communityTag}/tiers/{tierId}/form": {
      "get": {
        "summary": "Get the form schema for a tier",
        "security": [
          {
            "PublishableKey": [
              "READ_PUBLIC"
            ]
          }
        ],
        "parameters": [
          {
            "name": "communityTag",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Your community's tag (e.g. `bela-escala`, `orbis`)."
          },
          {
            "name": "tierId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Tier identifier from `GET /events/{slug}/tiers`."
          }
        ],
        "responses": {
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "502": {
            "$ref": "#/components/responses/UpstreamFailure"
          },
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "description": "Tier form schema \u2014 feed this to your form renderer to collect the fields the tier requires.",
                  "properties": {
                    "tierId": {
                      "type": "string"
                    },
                    "schema": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "id": {
                            "type": "string",
                            "description": "Stable field id; use as the key in `formAnswers`."
                          },
                          "type": {
                            "type": "string",
                            "enum": [
                              "SHORT_TEXT",
                              "LONG_TEXT",
                              "EMAIL",
                              "PHONE",
                              "NUMBER",
                              "URL",
                              "DROPDOWN",
                              "SINGLE_CHOICE",
                              "MULTIPLE_CHOICE",
                              "DATE",
                              "RATING"
                            ]
                          },
                          "label": {
                            "type": "string"
                          },
                          "required": {
                            "type": "boolean"
                          },
                          "options": {
                            "type": "array",
                            "items": {
                              "type": "object",
                              "properties": {
                                "id": {
                                  "type": "string"
                                },
                                "label": {
                                  "type": "string"
                                }
                              }
                            },
                            "description": "Only present for DROPDOWN / SINGLE_CHOICE / MULTIPLE_CHOICE."
                          }
                        }
                      }
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/communities/{communityTag}/articles": {
      "get": {
        "summary": "List published articles",
        "security": [
          {
            "PublishableKey": [
              "READ_PUBLIC"
            ]
          }
        ],
        "parameters": [
          {
            "name": "communityTag",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Your community's tag (e.g. `bela-escala`, `orbis`)."
          },
          {
            "name": "limit",
            "in": "query",
            "schema": {
              "type": "integer",
              "default": 20,
              "maximum": 100
            },
            "description": "Page size. Default 20, max 100."
          },
          {
            "name": "offset",
            "in": "query",
            "schema": {
              "type": "integer",
              "default": 0
            },
            "description": "Zero-based offset for pagination. Use either offset or cursor \u2014 not both."
          }
        ],
        "responses": {
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "502": {
            "$ref": "#/components/responses/UpstreamFailure"
          },
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/Article"
                  }
                }
              }
            }
          }
        }
      },
      "post": {
        "summary": "Create an article (DRAFT)",
        "security": [
          {
            "SecretKey": [
              "ADMIN"
            ]
          }
        ],
        "parameters": [
          {
            "name": "communityTag",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Your community's tag (e.g. `bela-escala`, `orbis`)."
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "title"
                ],
                "properties": {
                  "title": {
                    "type": "string",
                    "description": "Article title. Required, non-empty. Used to generate the URL slug."
                  },
                  "content": {
                    "type": "string",
                    "description": "Body as sanitized HTML. Defaults to empty string if omitted."
                  },
                  "excerpt": {
                    "type": "string",
                    "description": "Short summary for listings (max 500 chars). Optional."
                  },
                  "category": {
                    "type": "string",
                    "description": "Free-form category label (max 100 chars). Optional."
                  },
                  "bannerUrl": {
                    "type": "string",
                    "description": "Hero image URL. Required to subsequently PATCH the article to `status: PUBLISHED`."
                  }
                }
              }
            }
          }
        },
        "responses": {
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "502": {
            "$ref": "#/components/responses/UpstreamFailure"
          },
          "201": {
            "description": "Article created in DRAFT status. PATCH the article with `status: \"PUBLISHED\"` to make it visible via GET /articles.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Article"
                }
              }
            }
          }
        }
      }
    },
    "/communities/{communityTag}/articles/slug/{slug}": {
      "get": {
        "summary": "Get article by slug",
        "security": [
          {
            "PublishableKey": [
              "READ_PUBLIC"
            ]
          }
        ],
        "parameters": [
          {
            "name": "communityTag",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Your community's tag (e.g. `bela-escala`, `orbis`)."
          },
          {
            "name": "slug",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "URL slug from the event / article. Stable across edits."
          }
        ],
        "responses": {
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "502": {
            "$ref": "#/components/responses/UpstreamFailure"
          },
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Article"
                }
              }
            }
          }
        }
      }
    },
    "/communities/{communityTag}/products": {
      "get": {
        "summary": "List products",
        "security": [
          {
            "PublishableKey": [
              "READ_PUBLIC"
            ]
          }
        ],
        "parameters": [
          {
            "name": "communityTag",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Your community's tag (e.g. `bela-escala`, `orbis`)."
          },
          {
            "name": "limit",
            "in": "query",
            "schema": {
              "type": "integer",
              "default": 20,
              "maximum": 100
            },
            "description": "Page size. Default 20, max 100."
          },
          {
            "name": "offset",
            "in": "query",
            "schema": {
              "type": "integer",
              "default": 0
            },
            "description": "Zero-based offset for pagination. Use either offset or cursor \u2014 not both."
          }
        ],
        "responses": {
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "502": {
            "$ref": "#/components/responses/UpstreamFailure"
          },
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/Product"
                  }
                }
              }
            }
          }
        }
      }
    },
    "/communities/{communityTag}/products/{sku}": {
      "get": {
        "summary": "Get product by SKU",
        "security": [
          {
            "PublishableKey": [
              "READ_PUBLIC"
            ]
          }
        ],
        "parameters": [
          {
            "name": "communityTag",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Your community's tag (e.g. `bela-escala`, `orbis`)."
          },
          {
            "name": "sku",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Product SKU from `GET /products`."
          }
        ],
        "responses": {
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "502": {
            "$ref": "#/components/responses/UpstreamFailure"
          },
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Product"
                }
              }
            }
          }
        }
      }
    },
    "/communities/{communityTag}/members": {
      "get": {
        "summary": "List public members directory",
        "security": [
          {
            "PublishableKey": [
              "READ_PUBLIC"
            ]
          }
        ],
        "parameters": [
          {
            "name": "communityTag",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Your community's tag (e.g. `bela-escala`, `orbis`)."
          },
          {
            "name": "limit",
            "in": "query",
            "schema": {
              "type": "integer",
              "default": 20,
              "maximum": 100
            },
            "description": "Page size. Default 20, max 100."
          },
          {
            "name": "offset",
            "in": "query",
            "schema": {
              "type": "integer",
              "default": 0
            },
            "description": "Zero-based offset for pagination. Use either offset or cursor \u2014 not both."
          }
        ],
        "responses": {
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "502": {
            "$ref": "#/components/responses/UpstreamFailure"
          },
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/Member"
                  }
                }
              }
            }
          }
        }
      }
    },
    "/communities/{communityTag}/atlas/hotspots": {
      "get": {
        "summary": "List atlas hotspots",
        "security": [
          {
            "PublishableKey": [
              "READ_PUBLIC"
            ]
          }
        ],
        "parameters": [
          {
            "name": "communityTag",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Your community's tag (e.g. `bela-escala`, `orbis`)."
          }
        ],
        "responses": {
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "502": {
            "$ref": "#/components/responses/UpstreamFailure"
          },
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/AtlasHotspot"
                  }
                }
              }
            }
          }
        }
      }
    },
    "/communities/{communityTag}/atlas/hotspots/{hotspotId}": {
      "get": {
        "summary": "Get atlas hotspot detail",
        "security": [
          {
            "PublishableKey": [
              "READ_PUBLIC"
            ]
          }
        ],
        "parameters": [
          {
            "name": "communityTag",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Your community's tag (e.g. `bela-escala`, `orbis`)."
          },
          {
            "name": "hotspotId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Atlas hotspot identifier."
          }
        ],
        "responses": {
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "502": {
            "$ref": "#/components/responses/UpstreamFailure"
          },
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AtlasHotspot"
                }
              }
            }
          }
        }
      }
    },
    "/communities/{communityTag}/atlas/categories": {
      "get": {
        "summary": "List atlas categories",
        "security": [
          {
            "PublishableKey": [
              "READ_PUBLIC"
            ]
          }
        ],
        "parameters": [
          {
            "name": "communityTag",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Your community's tag (e.g. `bela-escala`, `orbis`)."
          }
        ],
        "responses": {
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "502": {
            "$ref": "#/components/responses/UpstreamFailure"
          },
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/AtlasCategory"
                  }
                }
              }
            }
          }
        }
      }
    },
    "/communities/{communityTag}/segments/public": {
      "get": {
        "summary": "List public segments (tiers)",
        "security": [
          {
            "PublishableKey": [
              "READ_PUBLIC"
            ]
          }
        ],
        "parameters": [
          {
            "name": "communityTag",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Your community's tag (e.g. `bela-escala`, `orbis`)."
          }
        ],
        "responses": {
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "502": {
            "$ref": "#/components/responses/UpstreamFailure"
          },
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/Segment"
                  }
                }
              }
            }
          }
        }
      }
    },
    "/communities/{communityTag}/segments/{segmentId}/check-email": {
      "get": {
        "summary": "Check whether an email is already registered for this segment",
        "security": [
          {
            "PublishableKey": [
              "READ_PUBLIC"
            ]
          }
        ],
        "parameters": [
          {
            "name": "communityTag",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Your community's tag (e.g. `bela-escala`, `orbis`)."
          },
          {
            "name": "segmentId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Segment identifier from `GET /segments/public`."
          },
          {
            "name": "email",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string",
              "format": "email"
            },
            "description": "Email address (RFC 5322)."
          }
        ],
        "responses": {
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "502": {
            "$ref": "#/components/responses/UpstreamFailure"
          },
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "exists": {
                      "type": "boolean"
                    },
                    "status": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/communities/{communityTag}/segments/{segmentId}/form": {
      "get": {
        "summary": "Get the segment's form schema",
        "security": [
          {
            "PublishableKey": [
              "READ_PUBLIC"
            ]
          }
        ],
        "parameters": [
          {
            "name": "communityTag",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Your community's tag (e.g. `bela-escala`, `orbis`)."
          },
          {
            "name": "segmentId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Segment identifier from `GET /segments/public`."
          }
        ],
        "responses": {
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "502": {
            "$ref": "#/components/responses/UpstreamFailure"
          },
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "description": "Segment form schema \u2014 feed this to your form renderer.",
                  "properties": {
                    "segmentId": {
                      "type": "string"
                    },
                    "schema": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "id": {
                            "type": "string",
                            "description": "Stable field id; use as the key in `formAnswers`."
                          },
                          "type": {
                            "type": "string",
                            "enum": [
                              "SHORT_TEXT",
                              "LONG_TEXT",
                              "EMAIL",
                              "PHONE",
                              "NUMBER",
                              "URL",
                              "DROPDOWN",
                              "SINGLE_CHOICE",
                              "MULTIPLE_CHOICE",
                              "DATE",
                              "RATING"
                            ]
                          },
                          "label": {
                            "type": "string"
                          },
                          "required": {
                            "type": "boolean"
                          }
                        }
                      }
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/communities/{communityTag}/segments/{segmentId}/submit": {
      "post": {
        "summary": "Submit the segment's form (= apply for membership)",
        "security": [
          {
            "SecretKey": [
              "WRITE_MEMBERS"
            ]
          }
        ],
        "parameters": [
          {
            "name": "communityTag",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Your community's tag (e.g. `bela-escala`, `orbis`)."
          },
          {
            "name": "segmentId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Segment identifier from `GET /segments/public`."
          }
        ],
        "responses": {
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "502": {
            "$ref": "#/components/responses/UpstreamFailure"
          },
          "200": {
            "description": "Application submitted; `member.requested` webhook fires",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "ok": {
                      "type": "boolean",
                      "description": "Always `true` on a 200 response."
                    },
                    "requestId": {
                      "type": "string",
                      "description": "Stable id for the membership request. Carried in the `member.requested` webhook payload."
                    },
                    "status": {
                      "type": "string",
                      "enum": [
                        "PENDING",
                        "ACCEPTED"
                      ],
                      "description": "PENDING for review-required communities; ACCEPTED when the segment is auto-approve."
                    }
                  }
                }
              }
            }
          }
        },
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "email",
                  "name",
                  "formAnswers"
                ],
                "properties": {
                  "email": {
                    "type": "string",
                    "format": "email",
                    "description": "Applicant's email. If a membership request for this email already exists for this segment, the response is 409 with the existing request id."
                  },
                  "name": {
                    "type": "string",
                    "description": "Applicant's display name. Required for downstream `member.requested` event payloads + email correspondence."
                  },
                  "formAnswers": {
                    "type": "object",
                    "description": "Form answers keyed by field id. Shape must match the segment's form schema (`GET /segments/{segmentId}/form`)."
                  }
                }
              }
            }
          }
        }
      }
    },
    "/communities/{communityTag}/rules/accept": {
      "post": {
        "summary": "Accept the community rules (per visitor / on first member action)",
        "security": [
          {
            "SecretKey": [
              "WRITE_MEMBERS"
            ]
          }
        ],
        "parameters": [
          {
            "name": "communityTag",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Your community's tag (e.g. `bela-escala`, `orbis`)."
          }
        ],
        "responses": {
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "502": {
            "$ref": "#/components/responses/UpstreamFailure"
          },
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "ok": {
                      "type": "boolean",
                      "description": "Always `true` on a 200 response."
                    }
                  }
                }
              }
            }
          }
        },
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "rulesVersion"
                ],
                "properties": {
                  "rulesVersion": {
                    "type": "integer",
                    "description": "Integer version of the rules document the visitor accepted. Get the current version from the community's storefront config."
                  }
                }
              }
            }
          }
        }
      }
    },
    "/communities/{communityTag}/members/{userId}/kick": {
      "post": {
        "summary": "Kick a member",
        "security": [
          {
            "SecretKey": [
              "WRITE_MEMBERS"
            ]
          }
        ],
        "parameters": [
          {
            "name": "communityTag",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Your community's tag (e.g. `bela-escala`, `orbis`)."
          },
          {
            "name": "userId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "User identifier from `GET /members`."
          }
        ],
        "responses": {
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "502": {
            "$ref": "#/components/responses/UpstreamFailure"
          },
          "200": {
            "description": "Member kicked; `member.kicked` webhook fires",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "ok": {
                      "type": "boolean",
                      "description": "Always `true` on a 200 response."
                    },
                    "kickedAt": {
                      "type": "string",
                      "format": "date-time",
                      "description": "Server timestamp of the action."
                    }
                  }
                }
              }
            }
          }
        },
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "reason": {
                    "type": "string",
                    "description": "Optional internal note about why the member was kicked. Stored on the membership audit trail; not surfaced to the kicked member."
                  }
                }
              }
            }
          }
        }
      }
    },
    "/communities/{communityTag}/members/{userId}/ban": {
      "post": {
        "summary": "Ban a member (irreversible)",
        "security": [
          {
            "SecretKey": [
              "WRITE_MEMBERS"
            ]
          }
        ],
        "parameters": [
          {
            "name": "communityTag",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Your community's tag (e.g. `bela-escala`, `orbis`)."
          },
          {
            "name": "userId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "User identifier from `GET /members`."
          }
        ],
        "responses": {
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "502": {
            "$ref": "#/components/responses/UpstreamFailure"
          },
          "200": {
            "description": "Member banned; `member.banned` webhook fires",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "ok": {
                      "type": "boolean",
                      "description": "Always `true` on a 200 response."
                    },
                    "bannedAt": {
                      "type": "string",
                      "format": "date-time",
                      "description": "Server timestamp. Bans are irreversible."
                    }
                  }
                }
              }
            }
          }
        },
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "reason": {
                    "type": "string",
                    "description": "Optional internal note. Bans are irreversible; the reason helps future admins understand prior decisions."
                  }
                }
              }
            }
          }
        }
      }
    },
    "/communities/{communityTag}/applications/{requestId}/approve": {
      "post": {
        "summary": "Approve a pending membership application",
        "security": [
          {
            "SecretKey": [
              "WRITE_MEMBERS"
            ]
          }
        ],
        "parameters": [
          {
            "name": "communityTag",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Your community's tag (e.g. `bela-escala`, `orbis`)."
          },
          {
            "name": "requestId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Membership request identifier (from the `member.requested` webhook or the admin app)."
          }
        ],
        "responses": {
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "502": {
            "$ref": "#/components/responses/UpstreamFailure"
          },
          "200": {
            "description": "`member.approved` webhook fires",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "ok": {
                      "type": "boolean",
                      "description": "Always `true` on a 200 response."
                    },
                    "membershipId": {
                      "type": "string",
                      "description": "Newly-created membership id (mirror of the `member.approved` event payload)."
                    },
                    "userId": {
                      "type": "string",
                      "description": "User id the membership was created for."
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/communities/{communityTag}/applications/{requestId}/reject": {
      "post": {
        "summary": "Reject a pending membership application",
        "security": [
          {
            "SecretKey": [
              "WRITE_MEMBERS"
            ]
          }
        ],
        "parameters": [
          {
            "name": "communityTag",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Your community's tag (e.g. `bela-escala`, `orbis`)."
          },
          {
            "name": "requestId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Membership request identifier (from the `member.requested` webhook or the admin app)."
          }
        ],
        "responses": {
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "502": {
            "$ref": "#/components/responses/UpstreamFailure"
          },
          "200": {
            "description": "`member.rejected` webhook fires",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "ok": {
                      "type": "boolean",
                      "description": "Always `true` on a 200 response."
                    }
                  }
                }
              }
            }
          }
        },
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "reason": {
                    "type": "string",
                    "description": "Optional internal note shown to other admins. Not sent to the applicant by default \u2014 wire an automation if you want a reject email."
                  }
                }
              }
            }
          }
        }
      }
    },
    "/communities/{communityTag}/segments/{segmentId}": {
      "patch": {
        "summary": "Update a segment (name / pricing / active)",
        "security": [
          {
            "SecretKey": [
              "ADMIN"
            ]
          }
        ],
        "parameters": [
          {
            "name": "communityTag",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Your community's tag (e.g. `bela-escala`, `orbis`)."
          },
          {
            "name": "segmentId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Segment identifier from `GET /segments/public`."
          }
        ],
        "responses": {
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "502": {
            "$ref": "#/components/responses/UpstreamFailure"
          },
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Segment"
                }
              }
            }
          }
        },
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "name": {
                    "type": "string",
                    "description": "New display name. Preserves the segment's id and existing memberships."
                  },
                  "priceMonthly": {
                    "type": "integer",
                    "nullable": true,
                    "description": "New monthly price in cents. Null disables monthly. Existing subscribers keep their current billing until renewal."
                  },
                  "priceQuarterly": {
                    "type": "integer",
                    "nullable": true,
                    "description": "New quarterly price in cents. Null disables quarterly."
                  },
                  "priceYearly": {
                    "type": "integer",
                    "nullable": true,
                    "description": "New yearly price in cents. Null disables yearly."
                  },
                  "isActive": {
                    "type": "boolean",
                    "description": "False = closed to new members (existing memberships unaffected). True = open for new applications."
                  }
                }
              }
            }
          }
        }
      }
    },
    "/communities/{communityTag}/broadcasts": {
      "post": {
        "summary": "Send a one-shot email broadcast (rate-limited 5/24h)",
        "security": [
          {
            "SecretKey": [
              "WRITE_BROADCASTS"
            ]
          }
        ],
        "parameters": [
          {
            "name": "communityTag",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Your community's tag (e.g. `bela-escala`, `orbis`)."
          }
        ],
        "responses": {
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "502": {
            "$ref": "#/components/responses/UpstreamFailure"
          },
          "200": {
            "description": "`broadcast.sent` webhook fires after delivery",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "ok": {
                      "type": "boolean",
                      "description": "Always `true` on a 200 response."
                    },
                    "broadcastId": {
                      "type": "string",
                      "description": "Stable id for the send. Carried in `broadcast.sent` webhook."
                    },
                    "recipientCount": {
                      "type": "integer",
                      "description": "Number of members targeted (pre-deliverability filtering)."
                    }
                  }
                }
              }
            }
          }
        },
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "subject",
                  "html",
                  "segmentId"
                ],
                "properties": {
                  "subject": {
                    "type": "string",
                    "description": "Email subject line. Personalization tokens like `{{name}}` are NOT processed here \u2014 wire a templated automation if you need that."
                  },
                  "html": {
                    "type": "string",
                    "description": "Email body as full HTML. Must be valid email-safe HTML (no `<script>`, inline styles preferred)."
                  },
                  "segmentId": {
                    "type": "string",
                    "description": "Target segment; null = all members."
                  },
                  "previewText": {
                    "type": "string",
                    "description": "Inbox preview (hidden text shown next to the subject in most clients). 90 chars max."
                  }
                }
              }
            }
          }
        }
      }
    },
    "/communities/{communityTag}/articles/{articleId}": {
      "patch": {
        "summary": "Update an article (incl. publish/unpublish)",
        "security": [
          {
            "SecretKey": [
              "ADMIN"
            ]
          }
        ],
        "parameters": [
          {
            "name": "communityTag",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Your community's tag (e.g. `bela-escala`, `orbis`)."
          },
          {
            "name": "articleId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Article identifier (uuid) from `POST /articles` response."
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "description": "Any subset of editable fields. Empty bodies are rejected with 400.",
                "properties": {
                  "title": {
                    "type": "string",
                    "description": "New title. Note: changing the title does NOT regenerate the slug \u2014 slugs are stable across edits."
                  },
                  "content": {
                    "type": "string",
                    "description": "New HTML body."
                  },
                  "excerpt": {
                    "type": "string",
                    "nullable": true,
                    "description": "New short summary; null to clear."
                  },
                  "category": {
                    "type": "string",
                    "nullable": true,
                    "description": "New category; null to clear."
                  },
                  "bannerUrl": {
                    "type": "string",
                    "nullable": true,
                    "description": "New banner image; null to clear. **Cannot be null if `status` becomes `PUBLISHED` in the same request** \u2014 banners are required for published articles."
                  },
                  "status": {
                    "type": "string",
                    "enum": [
                      "DRAFT",
                      "PUBLISHED"
                    ],
                    "description": "Workflow status. `PUBLISHED` makes the article visible via `GET /articles` + fires the `article.published` webhook. Setting back to `DRAFT` hides it again + fires `article.deleted` (legacy event name \u2014 fires on unpublish too)."
                  }
                }
              }
            }
          }
        },
        "responses": {
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "502": {
            "$ref": "#/components/responses/UpstreamFailure"
          },
          "200": {
            "description": "Article updated. Returns the full updated row.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Article"
                }
              }
            }
          }
        }
      }
    }
  }
}