{
  "openapi": "3.1.0",
  "info": {
    "title": "RSVPme.xyz API",
    "version": "v1",
    "description": "A small API for agent-assisted signup, event creation and publishing, and event status reads. Zero-click flow: start a signup intent, the host approves via one email, then poll /api/v1/signup/result for a one-time API key and create published events directly."
  },
  "servers": [
    {
      "url": "https://rsvpme.xyz"
    }
  ],
  "paths": {
    "/api/v1/health": {
      "get": {
        "summary": "API health",
        "responses": {
          "200": {
            "description": "API is reachable",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/HealthResponse" }
              }
            }
          }
        }
      }
    },
    "/api/v1/signup/start": {
      "post": {
        "summary": "Start an agent-assisted signup intent",
        "description": "Creates a short-lived intent and claim link for a human email address. This does not create an API key or publish an event.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": { "$ref": "#/components/schemas/SignupStartRequest" }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Intent created",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/SignupStartResponse" }
              }
            }
          },
          "400": { "$ref": "#/components/responses/BadRequest" },
          "422": { "$ref": "#/components/responses/ValidationFailed" },
          "500": { "$ref": "#/components/responses/ServerError" }
        }
      }
    },
    "/api/v1/signup-intents": {
      "post": {
        "summary": "Start an agent-assisted signup intent",
        "description": "Alias for POST /api/v1/signup/start.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": { "$ref": "#/components/schemas/SignupStartRequest" }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Intent created",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/SignupStartResponse" }
              }
            }
          },
          "400": { "$ref": "#/components/responses/BadRequest" },
          "422": { "$ref": "#/components/responses/ValidationFailed" },
          "500": { "$ref": "#/components/responses/ServerError" }
        }
      }
    },
    "/api/v1/signup/result": {
      "post": {
        "summary": "Poll the result of a signup intent",
        "description": "After the host approves via the email link, returns the published event's share link and — exactly once — a freshly minted API key to store as RSVPME_API_KEY. Authenticated by the intent's own token (no API key yet). Returns status \"pending\" until the host approves.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": { "$ref": "#/components/schemas/SignupResultRequest" }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Signup result (pending or ready)",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/SignupResultResponse" }
              }
            }
          },
          "400": { "$ref": "#/components/responses/BadRequest" },
          "404": { "$ref": "#/components/responses/NotFound" },
          "422": { "$ref": "#/components/responses/ValidationFailed" },
          "500": { "$ref": "#/components/responses/ServerError" }
        }
      }
    },
    "/api/v1/events": {
      "post": {
        "summary": "Create an event (optionally publish)",
        "description": "Creates an event for the API key owner's organization. Send publish:true to publish immediately and get a live share url; otherwise it is created as a draft.",
        "security": [{ "bearerAuth": [] }],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": { "$ref": "#/components/schemas/EventCreateRequest" }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Event created",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/EventCreateResponse" }
              }
            }
          },
          "400": { "$ref": "#/components/responses/BadRequest" },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "403": { "$ref": "#/components/responses/Forbidden" },
          "422": { "$ref": "#/components/responses/ValidationFailed" },
          "500": { "$ref": "#/components/responses/ServerError" }
        }
      }
    },
    "/api/v1/events/{id}/status": {
      "get": {
        "summary": "Read event status and RSVP counts",
        "security": [{ "bearerAuth": [] }],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": { "type": "string", "format": "uuid" }
          }
        ],
        "responses": {
          "200": {
            "description": "Event status and RSVP counts",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/EventStatusResponse" }
              }
            }
          },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "404": { "$ref": "#/components/responses/NotFound" },
          "500": { "$ref": "#/components/responses/ServerError" }
        }
      }
    }
  },
  "components": {
    "securitySchemes": {
      "bearerAuth": {
        "type": "http",
        "scheme": "bearer"
      }
    },
    "responses": {
      "BadRequest": {
        "description": "Invalid request JSON",
        "content": {
          "application/json": {
            "schema": { "$ref": "#/components/schemas/ApiError" }
          }
        }
      },
      "Unauthorized": {
        "description": "Missing, revoked, expired, or invalid API key",
        "content": {
          "application/json": {
            "schema": { "$ref": "#/components/schemas/ApiError" }
          }
        }
      },
      "Forbidden": {
        "description": "API key does not include the required scope",
        "content": {
          "application/json": {
            "schema": { "$ref": "#/components/schemas/ApiError" }
          }
        }
      },
      "NotFound": {
        "description": "Resource not found",
        "content": {
          "application/json": {
            "schema": { "$ref": "#/components/schemas/ApiError" }
          }
        }
      },
      "ValidationFailed": {
        "description": "Validation failed",
        "content": {
          "application/json": {
            "schema": { "$ref": "#/components/schemas/ApiError" }
          }
        }
      },
      "ServerError": {
        "description": "Server-side failure",
        "content": {
          "application/json": {
            "schema": { "$ref": "#/components/schemas/ApiError" }
          }
        }
      }
    },
    "schemas": {
      "ApiError": {
        "type": "object",
        "required": ["ok", "error"],
        "properties": {
          "ok": { "type": "boolean", "const": false },
          "error": { "type": "string" },
          "details": {}
        }
      },
      "HealthResponse": {
        "type": "object",
        "required": ["ok", "service", "version", "time"],
        "properties": {
          "ok": { "type": "boolean", "const": true },
          "service": { "type": "string", "const": "rsvpme.xyz" },
          "version": { "type": "string", "const": "v1" },
          "time": { "type": "string", "format": "date-time" }
        }
      },
      "SignupStartRequest": {
        "type": "object",
        "required": ["email"],
        "properties": {
          "email": { "type": "string", "format": "email" },
          "full_name": { "type": ["string", "null"], "maxLength": 120 },
          "organization_name": { "type": ["string", "null"], "maxLength": 120 },
          "source": { "type": ["string", "null"], "maxLength": 120 },
          "event_draft": { "$ref": "#/components/schemas/AgentEventDraft" }
        }
      },
      "AgentEventDraft": {
        "type": "object",
        "required": ["title", "description", "start_date"],
        "properties": {
          "title": { "type": "string", "minLength": 1, "maxLength": 120 },
          "description": { "type": "string", "minLength": 1 },
          "start_date": { "type": "string" },
          "time_zone": { "type": ["string", "null"], "example": "America/New_York" },
          "location_name": { "type": ["string", "null"], "maxLength": 160 },
          "address": { "type": ["string", "null"], "maxLength": 500 },
          "directions": { "type": ["string", "null"], "maxLength": 1000 }
        }
      },
      "SignupStartResponse": {
        "type": "object",
        "required": ["ok", "intent_id", "claim_url", "expires_at", "email_sent"],
        "properties": {
          "ok": { "type": "boolean", "const": true },
          "intent_id": { "type": "string", "format": "uuid" },
          "intent_token": { "type": "string", "description": "Pass to /api/v1/signup/result along with intent_id to poll for the API key and share link." },
          "claim_url": { "type": "string", "format": "uri" },
          "expires_at": { "type": "string", "format": "date-time" },
          "email_sent": { "type": "boolean" },
          "next": { "type": "object", "description": "Guidance for the agent's next call." }
        }
      },
      "SignupResultRequest": {
        "type": "object",
        "required": ["intent_id", "token"],
        "properties": {
          "intent_id": { "type": "string", "format": "uuid" },
          "token": { "type": "string", "description": "intent_token from the signup start response." }
        }
      },
      "SignupResultResponse": {
        "type": "object",
        "required": ["ok", "status"],
        "properties": {
          "ok": { "type": "boolean", "const": true },
          "status": { "type": "string", "enum": ["pending", "ready"] },
          "email_verified": { "type": "boolean" },
          "api_key": { "type": ["string", "null"], "description": "Plaintext key, returned exactly once. Store as RSVPME_API_KEY." },
          "api_key_prefix": { "type": ["string", "null"] },
          "api_key_issued": { "type": "boolean" },
          "store_as": { "type": "string", "const": "RSVPME_API_KEY" },
          "event": { "type": ["object", "null"], "description": "The published event, with url and dashboard_url." },
          "message": { "type": "string" }
        }
      },
      "EventCreateRequest": {
        "type": "object",
        "required": ["title", "description", "start_date", "time_zone", "location_name"],
        "properties": {
          "title": { "type": "string", "minLength": 1, "maxLength": 120 },
          "description": { "type": "string", "minLength": 1 },
          "start_date": { "type": "string" },
          "end_date": { "type": ["string", "null"] },
          "time_zone": { "type": "string", "example": "America/New_York" },
          "location_name": { "type": "string", "minLength": 1 },
          "address": { "type": ["string", "null"] },
          "directions": { "type": ["string", "null"] },
          "map_link": { "type": ["string", "null"], "format": "uri" },
          "rsvp_deadline": { "type": ["string", "null"] },
          "max_guests": { "type": ["integer", "null"], "minimum": 1 },
          "potluck_enabled": { "type": "boolean", "default": false },
          "potluck_required": { "type": "boolean", "default": false },
          "potluck_label": { "type": "string", "default": "What can you bring?" },
          "allow_plus_ones": { "type": "boolean", "default": true },
          "custom_message": { "type": ["string", "null"] },
          "theme_color": { "type": "string", "default": "#111827" },
          "publish": { "type": "boolean", "default": false, "description": "Publish immediately and return a live share url." },
          "status": { "type": "string", "enum": ["draft", "published"] }
        }
      },
      "EventCreateResponse": {
        "type": "object",
        "required": ["ok", "event"],
        "properties": {
          "ok": { "type": "boolean", "const": true },
          "event": {
            "type": "object",
            "required": ["id", "slug", "status", "url", "dashboard_url"],
            "properties": {
              "id": { "type": "string", "format": "uuid" },
              "slug": { "type": "string" },
              "status": { "type": "string", "enum": ["draft", "published"] },
              "url": { "type": "string", "format": "uri" },
              "dashboard_url": { "type": "string", "format": "uri" }
            }
          }
        }
      },
      "EventStatusResponse": {
        "type": "object",
        "required": ["ok", "event"],
        "properties": {
          "ok": { "type": "boolean", "const": true },
          "event": {
            "type": "object",
            "required": ["id", "slug", "title", "status", "start_date", "time_zone", "rsvp_count", "attending_count", "guest_total"],
            "properties": {
              "id": { "type": "string", "format": "uuid" },
              "slug": { "type": "string" },
              "title": { "type": "string" },
              "status": { "type": "string", "enum": ["draft", "published", "cancelled", "completed"] },
              "start_date": { "type": "string" },
              "time_zone": { "type": "string" },
              "rsvp_count": { "type": "integer", "minimum": 0 },
              "attending_count": { "type": "integer", "minimum": 0 },
              "guest_total": { "type": "integer", "minimum": 0 }
            }
          }
        }
      }
    }
  }
}
