{
  "openapi": "3.1.0",
  "info": {
    "title": "WERZ Public Consultation Booking",
    "version": "2.0.0",
    "description": "Book an authorized WERZ consultation without an API key. Fetch availability first and reuse a private UUID retry key. No internal management operations are exposed."
  },
  "externalDocs": {
    "description": "Public booking workflow",
    "url": "https://werz.ai/resources/agent-booking"
  },
  "servers": [
    {
      "url": "https://werz.ai"
    }
  ],
  "security": [],
  "paths": {
    "/agent-booking.json": {
      "get": {
        "operationId": "discoverPublicBooking",
        "summary": "Public booking manifest and workflow",
        "security": [],
        "responses": {
          "200": {
            "description": "Public instructions and endpoints",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          }
        }
      }
    },
    "/api/agent/public/availability": {
      "get": {
        "summary": "Find available consultation slots without an API key",
        "description": "Returns available 30-minute consultation slots. Queries are limited to a 30-day range and the public 60-day booking window.",
        "parameters": [
          {
            "name": "start",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "format": "date"
            }
          },
          {
            "name": "end",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "format": "date"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Currently offered slots. Availability is rechecked atomically during booking.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AvailabilityResponse"
                }
              }
            }
          },
          "400": {
            "description": "Invalid input. Read details for field errors and correct the payload.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "429": {
            "description": "Rate limited. Wait before retrying the same booking intent.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "headers": {
              "Retry-After": {
                "$ref": "#/components/headers/Retry-After"
              }
            }
          },
          "500": {
            "description": "Temporary error. Retry identical payload and idempotencyKey; do not assume nothing was booked.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        },
        "operationId": "getPublicConsultationAvailability",
        "security": []
      }
    },
    "/api/agent/public/booking": {
      "post": {
        "operationId": "bookPublicConsultation",
        "summary": "Book an authorized consultation without an API key",
        "description": "A successful submission reserves a real consultation and uses the normal booking confirmation/calendar workflow. SMS consent is not granted through this endpoint. After a timeout or server error, retry with the identical idempotencyKey and email. A used key returns its original reservation, even if a different slot was requested. Never create a fresh key solely to retry.",
        "security": [],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/PublicBookingRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Created or recovered reservation. Return the actual slot and any warning to the buyer.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PublicBookingResponse"
                }
              }
            }
          },
          "400": {
            "description": "Invalid input. Read details for field errors and correct the payload.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "403": {
            "description": "Cross-site browser submission is not allowed. Use a server-side HTTP client.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "409": {
            "description": "Slot taken, daily capacity reached, or booking still processing. Follow code; BOOKING_BUSY requires retrying the same key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "413": {
            "description": "Maximum body size is 16 KiB.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "415": {
            "description": "Content-Type must be application/json.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "429": {
            "description": "Rate limited. Wait before retrying the same booking intent.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "headers": {
              "Retry-After": {
                "$ref": "#/components/headers/Retry-After"
              }
            }
          },
          "500": {
            "description": "Temporary error. Retry identical payload and idempotencyKey; do not assume nothing was booked.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        }
      }
    }
  },
  "components": {
    "schemas": {
      "AvailabilityResponse": {
        "type": "object",
        "properties": {
          "availability": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "date": {
                  "type": "string",
                  "format": "date"
                },
                "slots": {
                  "type": "array",
                  "items": {
                    "type": "object",
                    "properties": {
                      "startTime": {
                        "type": "string",
                        "pattern": "^([01]\\d|2[0-3]):[0-5]\\d$"
                      },
                      "endTime": {
                        "type": "string",
                        "pattern": "^([01]\\d|2[0-3]):[0-5]\\d$"
                      },
                      "available": {
                        "type": "boolean"
                      }
                    },
                    "required": [
                      "startTime",
                      "endTime",
                      "available"
                    ]
                  }
                }
              },
              "required": [
                "date",
                "slots"
              ]
            }
          },
          "timezone": {
            "type": "string",
            "example": "America/Los_Angeles"
          },
          "slotDuration": {
            "type": "integer",
            "example": 30
          }
        },
        "required": [
          "availability",
          "timezone",
          "slotDuration"
        ]
      },
      "ErrorResponse": {
        "type": "object",
        "properties": {
          "error": {
            "type": "string"
          },
          "code": {
            "type": "string"
          },
          "details": {}
        },
        "required": [
          "error"
        ]
      },
      "PublicBookingRequest": {
        "type": "object",
        "additionalProperties": false,
        "properties": {
          "name": {
            "type": "string",
            "minLength": 2,
            "maxLength": 200
          },
          "email": {
            "type": "string",
            "format": "email",
            "maxLength": 255
          },
          "phone": {
            "type": "string",
            "description": "Real international callback number, including +country code; US and German numbers are supported. SMS consent is not implied."
          },
          "company": {
            "type": "string",
            "maxLength": 200
          },
          "date": {
            "type": "string",
            "format": "date"
          },
          "startTime": {
            "type": "string",
            "pattern": "^([01]\\d|2[0-3]):[0-5]\\d$"
          },
          "service": {
            "type": "string",
            "enum": [
              "VIDEO_PRODUCTION",
              "WEB_DEVELOPMENT",
              "MARKETING",
              "FULL_SERVICE"
            ]
          },
          "budget": {
            "type": "string",
            "enum": [
              "UNDER_5K",
              "5K_10K",
              "10K_25K",
              "25K_50K",
              "50K_PLUS",
              "NOT_SURE"
            ]
          },
          "notes": {
            "type": "string",
            "maxLength": 2000
          },
          "source": {
            "type": "string",
            "minLength": 1,
            "maxLength": 120,
            "description": "Caller label such as chatgpt, claude, or your workflow name. Self-reported attribution, not verified identity."
          },
          "idempotencyKey": {
            "type": "string",
            "format": "uuid",
            "description": "Fresh random UUID v4 per booking intent; reuse on every retry. Keep private."
          },
          "timezone": {
            "type": "string",
            "const": "America/Los_Angeles"
          },
          "userConfirmed": {
            "type": "boolean",
            "const": true,
            "description": "True only after the buyer authorizes booking this exact slot."
          },
          "locale": {
            "type": "string",
            "enum": [
              "en",
              "de"
            ]
          }
        },
        "required": [
          "name",
          "email",
          "phone",
          "date",
          "startTime",
          "timezone",
          "userConfirmed",
          "idempotencyKey",
          "source"
        ]
      },
      "PublicBookingResponse": {
        "type": "object",
        "properties": {
          "success": {
            "type": "boolean",
            "const": true
          },
          "idempotent": {
            "type": "boolean",
            "description": "True when the request matched an existing confirmed booking and the API returned that reservation instead of creating a duplicate."
          },
          "booking": {
            "type": "object",
            "properties": {
              "id": {
                "type": "string"
              },
              "date": {
                "type": "string",
                "format": "date"
              },
              "startTime": {
                "type": "string"
              },
              "endTime": {
                "type": "string"
              },
              "formattedDate": {
                "type": "string"
              },
              "formattedTime": {
                "type": "string"
              },
              "timezone": {
                "type": "string",
                "example": "America/Los_Angeles"
              },
              "status": {
                "type": "string",
                "example": "CONFIRMED"
              },
              "cancelUrl": {
                "type": "string",
                "format": "uri"
              },
              "meetUrl": {
                "type": [
                  "string",
                  "null"
                ],
                "format": "uri"
              },
              "calendarInviteSent": {
                "type": "boolean"
              },
              "calendarUrl": {
                "type": [
                  "string",
                  "null"
                ]
              }
            },
            "required": [
              "id",
              "date",
              "startTime",
              "endTime",
              "formattedDate",
              "formattedTime",
              "timezone",
              "status",
              "cancelUrl",
              "meetUrl"
            ]
          },
          "message": {
            "type": "string"
          },
          "warning": {
            "type": [
              "string",
              "null"
            ]
          },
          "emailAccepted": {
            "type": "boolean",
            "description": "Provider acceptance is not proof of inbox delivery."
          }
        },
        "required": [
          "success",
          "idempotent",
          "booking",
          "message"
        ]
      }
    },
    "headers": {
      "Retry-After": {
        "description": "Seconds until the current rate-limit window resets.",
        "schema": {
          "type": "integer"
        }
      }
    }
  }
}
