{
  "openapi": "3.1.0",
  "info": {
    "title": "Speedy Sticks API",
    "description": "API for retrieving healthcare price estimates and managing client integrations.",
    "version": "1.0.0",
    "contact": {
      "email": "info@speedysticks.com",
      "url": "https://api.speedysticks.com"
    }
  },
  "servers": [
    {
      "url": "https://api.speedysticks.com",
      "description": "Production"
    }
  ],
  "components": {
    "schemas": {
      "AuthResponse": {
        "type": "object",
        "properties": {
          "token": {
            "type": "string",
            "description": "JWT access token.",
            "example": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
          },
          "refreshToken": {
            "type": "string",
            "description": "Token used to obtain a new access token.",
            "example": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
          },
          "streamToken": {
            "type": "string",
            "description": "Token for stream-based services.",
            "example": "st_live_abc123xyz"
          }
        }
      },
      "Appointment": {
        "type": "object",
        "properties": {
          "id": {
            "type": "integer",
            "example": 1
          },
          "appointmentDateTime": {
            "type": "string",
            "format": "date-time",
            "example": "2024-01-15T09:00:00Z"
          },
          "appointmentTimeZone": {
            "type": "string",
            "example": "America/New_York"
          },
          "price": {
            "type": "number",
            "format": "decimal",
            "example": 150
          },
          "status": {
            "type": "string",
            "enum": [
              "Pending",
              "In Progress",
              "Completed",
              "Canceled",
              "Denied"
            ],
            "example": "Pending"
          },
          "notes": {
            "type": [
              "string",
              "null"
            ],
            "example": "Patient requested morning appointment"
          },
          "address": {
            "type": "string",
            "example": "123 Main St"
          },
          "city": {
            "type": "string",
            "example": "Phoenix"
          },
          "state": {
            "type": "string",
            "example": "AZ"
          },
          "zip": {
            "type": "string",
            "example": "85003"
          },
          "billingType": {
            "type": "string",
            "enum": [
              "Monthly Invoice",
              "Bill to Patient",
              "Bill to Account"
            ],
            "example": "Bill to Patient"
          }
        }
      },
      "Patient": {
        "type": "object",
        "properties": {
          "id": {
            "type": "integer",
            "example": 1
          },
          "firstName": {
            "type": "string",
            "example": "Jane"
          },
          "lastName": {
            "type": "string",
            "example": "Doe"
          },
          "dob": {
            "type": "string",
            "format": "date",
            "example": "1990-01-15"
          },
          "address": {
            "type": "string",
            "example": "123 Main St"
          },
          "city": {
            "type": "string",
            "example": "Phoenix"
          },
          "state": {
            "type": "string",
            "example": "AZ"
          },
          "zip": {
            "type": "string",
            "example": "85003"
          },
          "email": {
            "type": [
              "string",
              "null"
            ],
            "format": "email",
            "example": "jane.doe@example.com"
          },
          "phone": {
            "type": [
              "string",
              "null"
            ],
            "example": "6025554567"
          },
          "gender": {
            "type": "string",
            "enum": [
              "Male",
              "Female"
            ],
            "example": "Female"
          },
          "externalId": {
            "type": [
              "string",
              "null"
            ],
            "example": "ext_abc123"
          }
        }
      },
      "CancellationReason": {
        "type": "object",
        "properties": {
          "id": {
            "type": "integer",
            "example": 1
          },
          "name": {
            "type": "string",
            "example": "Scheduled by mistake"
          }
        }
      },
      "Webhook": {
        "type": "object",
        "properties": {
          "id": {
            "type": "integer",
            "example": 1
          },
          "url": {
            "type": "string",
            "example": "https://api.speedysticks.com/webhook-endpoint"
          },
          "events": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "example": [
              "appointment.approved.v1",
              "appointment.denied.v1"
            ]
          }
        }
      },
      "CostBreakdownItem": {
        "type": "object",
        "required": [
          "title",
          "amount"
        ],
        "properties": {
          "title": {
            "type": "string",
            "example": "Base Price (With Mileage)"
          },
          "amount": {
            "oneOf": [
              {
                "type": "number"
              },
              {
                "type": "string"
              }
            ],
            "description": "Monetary amount. Some pricing branches return a numeric string.",
            "example": "125.00"
          }
        }
      },
      "PriceEstimate": {
        "type": "object",
        "required": [
          "priceEstimate",
          "costBreakdown"
        ],
        "properties": {
          "priceEstimate": {
            "oneOf": [
              {
                "type": "number"
              },
              {
                "type": "string"
              }
            ],
            "description": "Final estimated appointment price.",
            "example": "150.00"
          },
          "serviceBasePrice": {
            "type": "number",
            "description": "Base price of the requested service. Omitted for certain account-specific pricing results.",
            "example": 99
          },
          "costBreakdown": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/CostBreakdownItem"
            }
          }
        }
      }
    },
    "securitySchemes": {
      "BearerAuth": {
        "type": "http",
        "scheme": "bearer",
        "bearerFormat": "JWT"
      },
      "ApiKeyAuth": {
        "type": "apiKey",
        "in": "header",
        "name": "ApiKey"
      },
      "ApiOrBearerAuth": {
        "type": "http",
        "scheme": "bearer",
        "bearerFormat": "JWT"
      }
    },
    "responses": {
      "Unauthorized": {
        "description": "Unauthorized.",
        "content": {
          "application/problem+json": {
            "schema": {
              "type": "object",
              "properties": {
                "title": {
                  "type": "string",
                  "example": "Unauthorized."
                }
              }
            }
          }
        }
      }
    }
  },
  "security": [
    {
      "BearerAuth": []
    }
  ],
  "paths": {
    "/request-code": {
      "post": {
        "summary": "Send a two factor authentication login code",
        "description": "Sends a one-time code to the provided email address.",
        "security": [],
        "tags": [
          "Auth"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "email"
                ],
                "properties": {
                  "email": {
                    "type": "string",
                    "format": "email",
                    "example": "jane.doe@example.com"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Code sent successfully."
          },
          "400": {
            "description": "Missing or invalid email."
          }
        }
      }
    },
    "/login": {
      "post": {
        "summary": "Login with two factor authentication code",
        "description": "Authenticates a user using their email and the one-time code they were sent.",
        "security": [],
        "tags": [
          "Auth"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "email",
                  "code"
                ],
                "properties": {
                  "email": {
                    "type": "string",
                    "format": "email",
                    "example": "jane.doe@example.com"
                  },
                  "code": {
                    "type": "string",
                    "example": "482910"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Authenticated successfully.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AuthResponse"
                }
              }
            }
          },
          "401": {
            "description": "Invalid or expired code."
          }
        }
      }
    },
    "/login-password": {
      "post": {
        "summary": "Login with password",
        "description": "Authenticates a user using their email and password.",
        "security": [],
        "tags": [
          "Auth"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "email",
                  "password"
                ],
                "properties": {
                  "email": {
                    "type": "string",
                    "format": "email",
                    "example": "jane.doe@example.com"
                  },
                  "password": {
                    "type": "string",
                    "format": "password",
                    "example": "supersecret123"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Authenticated successfully.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AuthResponse"
                }
              }
            }
          },
          "401": {
            "description": "Invalid credentials."
          }
        }
      }
    },
    "/api/key": {
      "get": {
        "summary": "Get API key",
        "description": "Returns the current API key and associated analytics for the authenticated client account.",
        "security": [
          {
            "BearerAuth": []
          }
        ],
        "tags": [
          "API Keys"
        ],
        "responses": {
          "200": {
            "description": "API key and analytics retrieved successfully.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "key": {
                      "type": "string",
                      "example": "sk_live_abc123xyz"
                    },
                    "analytics": {
                      "type": "object",
                      "description": "Usage analytics associated with the key."
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        }
      }
    },
    "/api/key/regenerate": {
      "post": {
        "summary": "Issue a new API key",
        "description": "Generates and returns a new API key for the authenticated client account, replacing the existing one.",
        "security": [
          {
            "BearerAuth": []
          }
        ],
        "tags": [
          "API Keys"
        ],
        "responses": {
          "201": {
            "description": "New API key issued successfully.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "key": {
                      "type": "string",
                      "example": "sk_live_def456uvw"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        }
      }
    },
    "/api/request-token": {
      "post": {
        "summary": "Request a bearer token",
        "description": "Exchange your API key for a short-lived bearer token. The token expires every 15 minutes and must be used in the Authorization header for all subsequent requests.",
        "security": [
          {
            "ApiKeyAuth": []
          }
        ],
        "tags": [
          "Auth"
        ],
        "responses": {
          "201": {
            "description": "Token generated successfully",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "message": {
                      "type": "string",
                      "example": "Token generated successfully"
                    },
                    "token": {
                      "type": "string",
                      "description": "JWT bearer token valid for 15 minutes",
                      "example": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Account is not in an active state."
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "description": "Invalid API key."
          }
        }
      }
    },
    "/api/patient": {
      "post": {
        "summary": "Create a patient",
        "description": "Creates a new patient record associated with the authenticated client account. If an externalId is provided and already exists for this account, a 409 is returned with the existing patient ID.",
        "security": [
          {
            "BearerAuth": []
          }
        ],
        "tags": [
          "Patients"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "firstName",
                  "lastName",
                  "dob",
                  "address",
                  "city",
                  "state",
                  "zip",
                  "gender",
                  "email"
                ],
                "properties": {
                  "firstName": {
                    "type": "string",
                    "example": "Jane"
                  },
                  "lastName": {
                    "type": "string",
                    "example": "Doe"
                  },
                  "dob": {
                    "type": "string",
                    "format": "date",
                    "example": "1990-01-15"
                  },
                  "address": {
                    "type": "string",
                    "example": "123 Main St"
                  },
                  "city": {
                    "type": "string",
                    "example": "Phoenix"
                  },
                  "state": {
                    "type": "string",
                    "example": "AZ"
                  },
                  "zip": {
                    "type": "string",
                    "example": "85003"
                  },
                  "email": {
                    "type": "string",
                    "format": "email",
                    "example": "jane.doe@example.com"
                  },
                  "phone": {
                    "type": "string",
                    "example": "6025554567"
                  },
                  "gender": {
                    "type": "string",
                    "enum": [
                      "Male",
                      "Female"
                    ],
                    "example": "Female"
                  },
                  "externalId": {
                    "type": "string",
                    "example": "ext_abc123"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Patient created successfully.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "patientId": {
                      "type": "integer",
                      "example": 1
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "409": {
            "description": "A patient with this external ID already exists.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "error": {
                      "type": "string",
                      "example": "A patient with this external ID already exists."
                    },
                    "patientId": {
                      "type": "integer",
                      "example": 1
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/api/patient/{id}": {
      "get": {
        "summary": "Get patient by ID",
        "description": "Retrieves a patient record by their internal ID. The patient must be assigned to the authenticated account.",
        "security": [
          {
            "BearerAuth": []
          }
        ],
        "tags": [
          "Patients"
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer"
            },
            "description": "The patient's internal ID"
          }
        ],
        "responses": {
          "200": {
            "description": "Patient retrieved successfully.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Patient"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "description": "Patient not found."
          }
        }
      },
      "patch": {
        "summary": "Update a patient",
        "description": "Updates an existing patient record. The externalId field cannot be changed after creation.",
        "security": [
          {
            "BearerAuth": []
          }
        ],
        "tags": [
          "Patients"
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer"
            },
            "description": "The patient's internal ID"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "firstName": {
                    "type": "string",
                    "example": "Jane"
                  },
                  "lastName": {
                    "type": "string",
                    "example": "Doe"
                  },
                  "dob": {
                    "type": "string",
                    "format": "date",
                    "example": "1990-01-15"
                  },
                  "address": {
                    "type": "string",
                    "example": "123 Main St"
                  },
                  "city": {
                    "type": "string",
                    "example": "Phoenix"
                  },
                  "state": {
                    "type": "string",
                    "example": "AZ"
                  },
                  "zip": {
                    "type": "string",
                    "example": "85003"
                  },
                  "email": {
                    "type": "string",
                    "format": "email",
                    "example": "jane.doe@example.com"
                  },
                  "phone": {
                    "type": "string",
                    "example": "6025554567"
                  },
                  "gender": {
                    "type": "string",
                    "enum": [
                      "Male",
                      "Female"
                    ],
                    "example": "Female"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Patient updated successfully.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "patientId": {
                      "type": "integer",
                      "example": 1
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "description": "Patient not found."
          }
        }
      }
    },
    "/api/patient/{patientId}/external-id": {
      "get": {
        "summary": "Get patient by external ID",
        "description": "Looks up a patient using their external ID from your system.",
        "security": [
          {
            "BearerAuth": []
          }
        ],
        "tags": [
          "Patients"
        ],
        "parameters": [
          {
            "name": "patientId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "The patient's external ID from your system"
          }
        ],
        "responses": {
          "200": {
            "description": "Patient retrieved successfully.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Patient"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "description": "Patient not found."
          }
        }
      }
    },
    "/api/appointment": {
      "get": {
        "summary": "List appointments",
        "description": "Returns all appointments associated with the authenticated client account.",
        "security": [
          {
            "BearerAuth": []
          }
        ],
        "tags": [
          "Appointments"
        ],
        "responses": {
          "200": {
            "description": "List of appointments retrieved successfully.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/Appointment"
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        }
      },
      "post": {
        "summary": "Book an appointment",
        "description": "Books a new appointment for a patient. Billing type determines payment requirements — bill-to-patient appointments require a valid Stripe paymentMethod ID collected via the setup intent flow. Bill-to-account falls back to the account card on file. Monthly invoice requires no payment method.",
        "security": [
          {
            "BearerAuth": []
          }
        ],
        "tags": [
          "Appointments"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "address",
                  "city",
                  "state",
                  "zip",
                  "patientId",
                  "billingType",
                  "appointmentDateTime",
                  "serviceId"
                ],
                "properties": {
                  "address": {
                    "type": "string",
                    "example": "123 Main St"
                  },
                  "city": {
                    "type": "string",
                    "example": "Phoenix"
                  },
                  "state": {
                    "type": "string",
                    "example": "AZ"
                  },
                  "zip": {
                    "type": "string",
                    "example": "85003"
                  },
                  "patientId": {
                    "type": "integer",
                    "example": 1
                  },
                  "billingType": {
                    "type": "string",
                    "enum": [
                      "Bill to Patient",
                      "Bill to Account",
                      "Monthly Invoice"
                    ],
                    "example": "Bill to Patient"
                  },
                  "paymentMethod": {
                    "type": "string",
                    "description": "Stripe payment method ID. Required when billingType is \"Bill to Patient\".",
                    "example": "pm_1TOn5vKR1SKMkoDmhkksYv2S"
                  },
                  "appointmentDateTime": {
                    "type": "string",
                    "format": "date-time",
                    "example": "2026-04-29T16:00:00.000Z"
                  },
                  "serviceId": {
                    "type": "integer",
                    "example": 1
                  },
                  "notes": {
                    "type": "string",
                    "example": "Patient prefers morning appointments."
                  }
                }
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Appointment booked successfully.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Appointment"
                }
              }
            }
          },
          "400": {
            "description": "Invalid request — bad billing type, missing payment method, invalid address, or past appointment time."
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "description": "Patient or service not found."
          }
        }
      }
    },
    "/api/appointment/{id}": {
      "get": {
        "summary": "Get appointment by ID",
        "description": "Retrieves a single appointment by ID. The appointment must belong to the authenticated client account.",
        "security": [
          {
            "BearerAuth": []
          }
        ],
        "tags": [
          "Appointments"
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer"
            },
            "description": "The appointment ID"
          }
        ],
        "responses": {
          "200": {
            "description": "Appointment retrieved successfully.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Appointment"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "description": "Appointment not found."
          }
        }
      }
    },
    "/api/cancel-appointment": {
      "post": {
        "summary": "Cancel an appointment",
        "description": "Cancels an existing appointment. The appointment must be in a Pending or Confirmed state and more than 3 hours away. A cancellation reason is required, and if the reason is \"Other\" a note must also be provided. A $50 cancellation fee applies.",
        "security": [
          {
            "BearerAuth": []
          }
        ],
        "tags": [
          "Appointments"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "appointmentId",
                  "cancellationReasonId"
                ],
                "properties": {
                  "appointmentId": {
                    "type": "integer",
                    "example": 1
                  },
                  "cancellationReasonId": {
                    "type": "integer",
                    "example": 3
                  },
                  "note": {
                    "type": "string",
                    "example": "Patient called to explain specific circumstances."
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Appointment cancelled successfully.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "message": {
                      "type": "string",
                      "example": "Appointment canceled successfully"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Appointment cannot be cancelled (past, within 3 hours, or already cancelled/completed)."
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "description": "Appointment or cancellation reason not found."
          }
        }
      }
    },
    "/api/cancellation-reasons": {
      "get": {
        "summary": "Get cancellation reasons",
        "description": "Returns a list of all available cancellation reasons to be used when cancelling an appointment.",
        "security": [
          {
            "BearerAuth": []
          }
        ],
        "tags": [
          "Appointments"
        ],
        "responses": {
          "200": {
            "description": "Cancellation reasons retrieved successfully.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/CancellationReason"
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        }
      }
    },
    "/api/service-area/check": {
      "get": {
        "summary": "Check zip code serviceability",
        "description": "Returns whether Speedy Sticks can service the provided zip code based on nearby phlebotomist availability.",
        "security": [
          {
            "BearerAuth": []
          }
        ],
        "tags": [
          "Service Area"
        ],
        "parameters": [
          {
            "name": "zip",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string",
              "pattern": "^\\d{5}$"
            },
            "description": "A valid 5-digit US zip code",
            "example": "10014"
          }
        ],
        "responses": {
          "200": {
            "description": "Serviceability check completed.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "serviceable": {
                      "type": "boolean",
                      "example": true
                    },
                    "message": {
                      "type": "string",
                      "example": "We service this area"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Invalid or missing zip code."
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        }
      }
    },
    "/api/webhooks/events": {
      "get": {
        "summary": "Get webhook events",
        "description": "Returns a list of all supported webhook event types available for subscription.",
        "security": [
          {
            "BearerAuth": []
          }
        ],
        "tags": [
          "Webhooks"
        ],
        "responses": {
          "200": {
            "description": "Events retrieved successfully",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "type": "string"
                  },
                  "example": [
                    "appointment.approved.v1",
                    "appointment.denied.v1",
                    "appointment.process.v1",
                    "appointment.completed.v1"
                  ]
                }
              }
            }
          }
        }
      }
    },
    "/api/webhooks": {
      "get": {
        "summary": "Get active webhooks",
        "description": "Returns a list of all active webhooks associated with your account.",
        "security": [
          {
            "BearerAuth": []
          }
        ],
        "tags": [
          "Webhooks"
        ],
        "responses": {
          "200": {
            "description": "Webhooks returned successfully",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/Webhook"
                  }
                }
              }
            }
          }
        }
      },
      "post": {
        "summary": "Create a webhook",
        "description": "Register a new webhook endpoint to receive real-time event notifications.",
        "security": [
          {
            "BearerAuth": []
          }
        ],
        "tags": [
          "Webhooks"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "url",
                  "events"
                ],
                "properties": {
                  "url": {
                    "type": "string",
                    "example": "https://webhook.site/624bbe8c-8939-4db0-952b-8590c7690dc9"
                  },
                  "events": {
                    "type": "array",
                    "items": {
                      "type": "string"
                    },
                    "example": [
                      "appointment.approved.v1",
                      "appointment.denied.v1"
                    ]
                  }
                }
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Webhook created successfully",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Webhook"
                }
              }
            }
          },
          "400": {
            "description": "Invalid request body"
          }
        }
      }
    },
    "/api/webhooks/{id}": {
      "get": {
        "summary": "Get webhook by ID",
        "description": "Retrieves a single webhook by ID.",
        "security": [
          {
            "BearerAuth": []
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "description": "Webhook ID",
            "required": true,
            "schema": {
              "type": "integer"
            }
          }
        ],
        "tags": [
          "Webhooks"
        ],
        "responses": {
          "200": {
            "description": "Webhook retrieved successfully",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Webhook"
                }
              }
            }
          }
        }
      },
      "delete": {
        "summary": "Delete a webhook",
        "description": "Permanently removes a webhook by ID.",
        "security": [
          {
            "BearerAuth": []
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "description": "Webhook ID",
            "required": true,
            "schema": {
              "type": "integer"
            }
          }
        ],
        "tags": [
          "Webhooks"
        ],
        "responses": {
          "200": {
            "description": "Webhook deleted successfully"
          },
          "404": {
            "description": "Webhook not found"
          }
        }
      },
      "patch": {
        "summary": "Update a webhook",
        "description": "Update the URL or subscribed events for an existing webhook.",
        "security": [
          {
            "BearerAuth": []
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "description": "Webhook ID",
            "required": true,
            "schema": {
              "type": "integer"
            }
          }
        ],
        "tags": [
          "Webhooks"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "url": {
                    "type": "string",
                    "example": "https://webhook.site/624bbe8c-8939-4db0-952b-8590c7690dc9"
                  },
                  "events": {
                    "type": "array",
                    "items": {
                      "type": "string"
                    },
                    "example": [
                      "appointment.approved.v1",
                      "appointment.denied.v1"
                    ]
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Webhook updated successfully",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Webhook"
                }
              }
            }
          },
          "404": {
            "description": "Webhook not found"
          }
        }
      }
    },
    "/api/booking/setup-intent": {
      "post": {
        "summary": "Create a stripe setup intent",
        "description": "Creates a Stripe SetupIntent for collecting a patient's card on file. Only required for bill-to-patient appointments. Returns a client secret and publishable key to initialize Stripe.js on the client side.",
        "security": [
          {
            "BearerAuth": []
          }
        ],
        "tags": [
          "Payments"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "patientId"
                ],
                "properties": {
                  "patientId": {
                    "type": "integer",
                    "example": 1
                  }
                }
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Setup intent created successfully.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "setupIntentSecret": {
                      "type": "string",
                      "description": "The Stripe client secret used to initialize the payment element on the frontend.",
                      "example": "seti_1Jv3iK_secret_abc123"
                    },
                    "publishableKey": {
                      "type": "string",
                      "description": "Your Stripe publishable key for initializing Stripe.js.",
                      "example": "pk_live_abc123"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "description": "Patient not found."
          }
        }
      }
    },
    "/api/price-check": {
      "get": {
        "summary": "Get an appointment price estimate",
        "description": "Calculates an appointment price estimate based on the selected service, number of patients, location, requested date, and authenticated client account.",
        "operationId": "getPriceCheck",
        "security": [
          {
            "BearerAuth": []
          }
        ],
        "tags": [
          "Appointments"
        ],
        "parameters": [
          {
            "name": "serviceId",
            "in": "query",
            "required": true,
            "description": "ID of the requested service.",
            "schema": {
              "oneOf": [
                {
                  "type": "integer"
                },
                {
                  "type": "string"
                }
              ]
            },
            "example": 1
          },
          {
            "name": "amountOfPeople",
            "in": "query",
            "required": true,
            "description": "Number of patients included in the appointment.",
            "schema": {
              "oneOf": [
                {
                  "type": "integer",
                  "minimum": 1
                },
                {
                  "type": "string"
                }
              ]
            },
            "example": 2
          },
          {
            "name": "location",
            "in": "query",
            "required": true,
            "description": "Full appointment address, formatted as address, city, state, and ZIP code.",
            "schema": {
              "type": "string"
            },
            "example": "123 Main St, Phoenix, AZ, 85003"
          },
          {
            "name": "selectedDate",
            "in": "query",
            "required": true,
            "description": "Requested appointment date and time.",
            "schema": {
              "type": "string",
              "format": "date-time"
            },
            "example": "2026-08-24T14:00:00.000Z"
          }
        ],
        "responses": {
          "200": {
            "description": "Price estimate calculated successfully.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "priceEstimate",
                    "costBreakdown"
                  ],
                  "properties": {
                    "priceEstimate": {
                      "oneOf": [
                        {
                          "type": "number"
                        },
                        {
                          "type": "string"
                        }
                      ],
                      "description": "Final estimated appointment price.",
                      "example": "150.00"
                    },
                    "serviceBasePrice": {
                      "type": "number",
                      "description": "Base price of the requested service. This field may be omitted for account-specific flat pricing.",
                      "example": 99
                    },
                    "costBreakdown": {
                      "type": "array",
                      "description": "Individual charges included in the estimate.",
                      "items": {
                        "type": "object",
                        "required": [
                          "title",
                          "amount"
                        ],
                        "properties": {
                          "title": {
                            "type": "string",
                            "example": "Base Price (With Mileage)"
                          },
                          "amount": {
                            "oneOf": [
                              {
                                "type": "number"
                              },
                              {
                                "type": "string"
                              }
                            ],
                            "description": "Charge amount. Some pricing branches return a numeric string.",
                            "example": "125.00"
                          }
                        }
                      }
                    }
                  }
                },
                "examples": {
                  "standardEstimate": {
                    "summary": "Standard estimate",
                    "value": {
                      "priceEstimate": "150.00",
                      "serviceBasePrice": 99,
                      "costBreakdown": [
                        {
                          "title": "Base Price (With Mileage)",
                          "amount": "125.00"
                        },
                        {
                          "title": "x1 Additional People",
                          "amount": 25
                        }
                      ]
                    }
                  },
                  "accountFlatPrice": {
                    "summary": "Client account flat price",
                    "value": {
                      "priceEstimate": 85,
                      "costBreakdown": [
                        {
                          "title": "Account Flat Price",
                          "amount": "85.00"
                        }
                      ]
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "One or more required query parameters are missing or invalid."
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "description": "The requested service does not exist."
          },
          "500": {
            "description": "The selected date is in the past or pricing could not be calculated."
          }
        }
      }
    },
    "/api/services": {
      "get": {
        "summary": "List appointment services",
        "description": "Returns all services available for appointment booking and price estimates.",
        "operationId": "getServices",
        "security": [
          {
            "BearerAuth": []
          }
        ],
        "tags": [
          "Appointments"
        ],
        "responses": {
          "200": {
            "description": "Services retrieved successfully.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "type": "object",
                    "required": [
                      "id",
                      "serviceName",
                      "price"
                    ],
                    "properties": {
                      "id": {
                        "type": "integer",
                        "description": "Unique service ID.",
                        "example": 1
                      },
                      "serviceName": {
                        "type": "string",
                        "description": "Display name of the service.",
                        "example": "Mobile Blood Draw"
                      },
                      "description": {
                        "type": [
                          "string",
                          "null"
                        ],
                        "description": "Description of the service.",
                        "example": "In-home specimen collection by a mobile phlebotomist."
                      },
                      "price": {
                        "oneOf": [
                          {
                            "type": "number"
                          },
                          {
                            "type": "string"
                          }
                        ],
                        "description": "Base price of the service.",
                        "example": "99.00"
                      }
                    }
                  }
                },
                "example": [
                  {
                    "id": 1,
                    "serviceName": "Mobile Blood Draw",
                    "description": "In-home specimen collection by a mobile phlebotomist.",
                    "price": "99.00"
                  },
                  {
                    "id": 2,
                    "serviceName": "Specimen Pickup",
                    "description": "Pickup and transportation of prepared specimens.",
                    "price": "75.00"
                  }
                ]
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        }
      }
    }
  },
  "tags": [
    {
      "name": "Auth",
      "description": "Authentication endpoints for logging in and obtaining tokens."
    },
    {
      "name": "API Keys",
      "description": "Manage your Speedy Sticks API key and view usage analytics."
    },
    {
      "name": "Patients",
      "description": "Manage patients associated with your account."
    },
    {
      "name": "Appointments",
      "description": "View and manage appointments."
    },
    {
      "name": "Webhooks",
      "description": "Receive real-time HTTP notifications when key events occur in your account. Register endpoint URLs and subscribe to specific events to keep your systems in sync automatically."
    },
    {
      "name": "Service Area",
      "description": "Determine if we can service a provided area."
    },
    {
      "name": "Payments",
      "description": "Manage patient payment methods and Stripe setup intents for bill-to-patient appointments."
    }
  ]
}