{
  "openapi": "3.0.3",
  "info": {
    "title": "PulseESM API",
    "description": "API-first Experience Sampling Method (ESM) platform for behavioral researchers. Build surveys, schedule signals, collect momentary data, and export results — all via API.\n\n## Authentication\n\nPulseESM uses two authentication methods:\n\n- **Researcher API Key** (`sk-...`): For study management, data export, and participant management. Pass as `Authorization: Bearer sk-your-api-key`.\n- **Participant JWT Token**: For participant-facing endpoints (survey responses, progress). Pass as `Authorization: Bearer eyJhbGci...`.\n\n## Quick Start\n\n1. `POST /v1/auth/signup` — Create account\n2. Verify email (check inbox)\n3. `POST /v1/auth/regenerate-key` — Get API key\n4. `POST /v1/studies` — Create a study\n5. `POST /v1/studies/{id}/items` — Add survey items (or `POST /v1/studies/{id}/import` to build them from a Word/PDF survey)\n6. `POST /v1/studies/{id}/participants` — Enroll participants\n7. `POST /v1/studies/{id}/deploy` — Deploy and start collecting data\n8. `GET /v1/studies/{id}/responses` — Export data\n\n## Rate Limits\n\n- Auth endpoints: 5 requests/minute\n- Bulk operations: 20 requests/minute\n- Export endpoints: 10 requests/minute\n- General API: 100 requests/minute",
    "version": "1.0.0",
    "contact": {
      "name": "PulseESM",
      "url": "https://pulseesm.com"
    },
    "license": {
      "name": "Proprietary"
    }
  },
  "servers": [
    {
      "url": "https://pulseesm-production.up.railway.app",
      "description": "Production"
    }
  ],
  "security": [],
  "tags": [
    {
      "name": "Auth",
      "description": "Researcher account management and API key lifecycle"
    },
    {
      "name": "Studies",
      "description": "Create and manage ESM studies"
    },
    {
      "name": "Items",
      "description": "Survey items (questions) within a study"
    },
    {
      "name": "Participants",
      "description": "Enroll and manage study participants"
    },
    {
      "name": "Signals",
      "description": "Scheduled notifications for participants"
    },
    {
      "name": "Responses",
      "description": "Export response data and analytics"
    },
    {
      "name": "Webhooks",
      "description": "Real-time event notifications"
    },
    {
      "name": "Templates",
      "description": "Pre-defined study templates"
    },
    {
      "name": "Collaborators",
      "description": "Study collaboration management"
    },
    {
      "name": "Participant API",
      "description": "Participant-facing endpoints (JWT auth)"
    },
    {
      "name": "Activity",
      "description": "Study audit trail"
    }
  ],
  "paths": {
    "/v1/auth/signup": {
      "post": {
        "tags": [
          "Auth"
        ],
        "summary": "Create researcher account",
        "description": "Register a new researcher account. A verification email will be sent. API key is issued after email verification.",
        "operationId": "signup",
        "x-rateLimit": "5/min",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "email",
                  "password",
                  "name"
                ],
                "properties": {
                  "email": {
                    "type": "string",
                    "format": "email",
                    "example": "researcher@university.edu"
                  },
                  "password": {
                    "type": "string",
                    "minLength": 8,
                    "maxLength": 128,
                    "example": "securepassword123"
                  },
                  "name": {
                    "type": "string",
                    "minLength": 1,
                    "maxLength": 255,
                    "example": "Dr. Smith"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Account created",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "message": {
                      "type": "string",
                      "example": "Account created! Please check your email to verify and get your API key."
                    },
                    "researcher": {
                      "$ref": "#/components/schemas/Researcher"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/ValidationError"
          },
          "409": {
            "description": "Email already registered",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "Email already registered",
                  "code": "DUPLICATE_EMAIL"
                }
              }
            }
          }
        }
      }
    },
    "/v1/auth/login": {
      "post": {
        "tags": [
          "Auth"
        ],
        "summary": "Login",
        "description": "Verify credentials. Does not return the API key (it's hashed). Use `/v1/auth/regenerate-key` to get a new key.",
        "operationId": "login",
        "x-rateLimit": "5/min",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "email",
                  "password"
                ],
                "properties": {
                  "email": {
                    "type": "string",
                    "format": "email"
                  },
                  "password": {
                    "type": "string"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Login successful",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "researcher": {
                      "$ref": "#/components/schemas/Researcher"
                    },
                    "message": {
                      "type": "string",
                      "example": "Login successful. Use /v1/auth/regenerate-key with your password to get a new API key."
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/AuthFailed"
          }
        }
      }
    },
    "/v1/auth/regenerate-key": {
      "post": {
        "tags": [
          "Auth"
        ],
        "summary": "Regenerate API key",
        "description": "Generate a new API key using email + password. Invalidates the previous key. Save the returned key — it will not be shown again.",
        "operationId": "regenerateKey",
        "x-rateLimit": "5/min",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "email",
                  "password"
                ],
                "properties": {
                  "email": {
                    "type": "string",
                    "format": "email"
                  },
                  "password": {
                    "type": "string"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Key regenerated",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "message": {
                      "type": "string"
                    },
                    "api_key": {
                      "type": "string",
                      "example": "sk-abc123def456..."
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/AuthFailed"
          }
        }
      }
    },
    "/v1/auth/rotate-key": {
      "post": {
        "tags": [
          "Auth"
        ],
        "summary": "Rotate API key (authenticated)",
        "description": "Rotate to a new API key using current key authentication. Old key is invalidated.",
        "operationId": "rotateKey",
        "security": [
          {
            "apiKey": []
          }
        ],
        "responses": {
          "200": {
            "description": "Key rotated",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "message": {
                      "type": "string"
                    },
                    "api_key": {
                      "type": "string",
                      "example": "sk-newkey789..."
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        }
      }
    },
    "/v1/auth/password": {
      "put": {
        "tags": [
          "Auth"
        ],
        "summary": "Change password",
        "description": "Change password for the authenticated researcher.",
        "operationId": "changePassword",
        "security": [
          {
            "apiKey": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "currentPassword",
                  "newPassword"
                ],
                "properties": {
                  "currentPassword": {
                    "type": "string"
                  },
                  "newPassword": {
                    "type": "string",
                    "minLength": 8,
                    "maxLength": 128
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Password changed",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "message": {
                      "type": "string",
                      "example": "Password changed successfully"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/AuthFailed"
          }
        }
      }
    },
    "/v1/auth/forgot-password": {
      "post": {
        "tags": [
          "Auth"
        ],
        "summary": "Request password reset",
        "description": "Send a password reset email. Always returns success to not reveal email existence.",
        "operationId": "forgotPassword",
        "x-rateLimit": "5/min",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "email"
                ],
                "properties": {
                  "email": {
                    "type": "string",
                    "format": "email"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Reset email sent (if account exists)",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "message": {
                      "type": "string",
                      "example": "If the email is registered, a password reset link has been sent."
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/v1/auth/reset-password": {
      "post": {
        "tags": [
          "Auth"
        ],
        "summary": "Reset password with token",
        "description": "Reset password using a token from the reset email. Token expires after 1 hour.",
        "operationId": "resetPassword",
        "x-rateLimit": "5/min",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "token",
                  "newPassword"
                ],
                "properties": {
                  "token": {
                    "type": "string",
                    "description": "Reset token from email"
                  },
                  "newPassword": {
                    "type": "string",
                    "minLength": 8,
                    "maxLength": 128
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Password reset",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "message": {
                      "type": "string",
                      "example": "Password has been reset successfully. You can now log in."
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Invalid or expired token",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "Invalid or expired reset token",
                  "code": "TOKEN_EXPIRED"
                }
              }
            }
          }
        }
      }
    },
    "/v1/auth/verify": {
      "get": {
        "tags": [
          "Auth"
        ],
        "summary": "Verify email",
        "description": "Verify researcher email address via link sent in email. Redirects to web app with a one-time claim token.",
        "operationId": "verifyEmail",
        "parameters": [
          {
            "name": "token",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Verification token from email"
          }
        ],
        "responses": {
          "302": {
            "description": "Redirects to /welcome?claim=TOKEN"
          },
          "400": {
            "$ref": "#/components/responses/ValidationError"
          }
        }
      }
    },
    "/v1/auth/claim-key": {
      "post": {
        "tags": [
          "Auth"
        ],
        "summary": "Claim API key",
        "description": "Exchange a one-time claim token (from email verification redirect) for the API key. Token expires in 10 minutes.",
        "operationId": "claimKey",
        "x-rateLimit": "5/min",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "token"
                ],
                "properties": {
                  "token": {
                    "type": "string",
                    "description": "Claim token from verification redirect"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "API key claimed",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "researcher": {
                      "$ref": "#/components/schemas/Researcher"
                    },
                    "api_key": {
                      "type": "string",
                      "example": "sk-your-new-api-key"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Invalid or expired claim token",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v1/auth/resend-verification": {
      "post": {
        "tags": [
          "Auth"
        ],
        "summary": "Resend verification email",
        "description": "Resend the email verification link. Returns success regardless of email existence.",
        "operationId": "resendVerification",
        "x-rateLimit": "5/min",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "email"
                ],
                "properties": {
                  "email": {
                    "type": "string",
                    "format": "email"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Verification email sent",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "message": {
                      "type": "string",
                      "example": "Verification email sent."
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/v1/studies": {
      "post": {
        "tags": [
          "Studies"
        ],
        "summary": "Create study",
        "description": "Create a new ESM study with schedule configuration. Email verification required.",
        "operationId": "createStudy",
        "security": [
          {
            "apiKey": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CreateStudyRequest"
              },
              "example": {
                "title": "Daily Mood Study",
                "description": "Tracking daily mood variations across two weeks",
                "schedule": {
                  "type": "signal-contingent",
                  "signals_per_day": 5,
                  "window": {
                    "start": "09:00",
                    "end": "21:00"
                  },
                  "min_interval_minutes": 30,
                  "duration_days": 14,
                  "expiry_minutes": 60
                }
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Study created",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Study"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/ValidationError"
          },
          "403": {
            "description": "Email not verified",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "Email verification required to create studies",
                  "code": "EMAIL_NOT_VERIFIED"
                }
              }
            }
          }
        }
      },
      "get": {
        "tags": [
          "Studies"
        ],
        "summary": "List studies",
        "description": "List all studies owned by the authenticated researcher.",
        "operationId": "listStudies",
        "security": [
          {
            "apiKey": []
          }
        ],
        "parameters": [
          {
            "name": "status",
            "in": "query",
            "schema": {
              "type": "string",
              "enum": [
                "draft",
                "active",
                "paused",
                "completed"
              ]
            },
            "description": "Filter by status"
          }
        ],
        "responses": {
          "200": {
            "description": "Study list",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "studies": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Study"
                      }
                    },
                    "count": {
                      "type": "integer"
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/v1/studies/{id}": {
      "get": {
        "tags": [
          "Studies"
        ],
        "summary": "Get study details",
        "description": "Get study details including items and participant count.",
        "operationId": "getStudy",
        "security": [
          {
            "apiKey": []
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Study details",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Study"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "items": {
                          "type": "array",
                          "items": {
                            "$ref": "#/components/schemas/Item"
                          }
                        },
                        "participant_count": {
                          "type": "integer"
                        }
                      }
                    }
                  ]
                }
              }
            }
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      },
      "patch": {
        "tags": [
          "Studies"
        ],
        "summary": "Update study",
        "description": "Update study settings. Accepts top-level fields or nested schedule object.",
        "operationId": "updateStudy",
        "security": [
          {
            "apiKey": []
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "title": {
                    "type": "string",
                    "maxLength": 500
                  },
                  "description": {
                    "type": "string",
                    "maxLength": 5000
                  },
                  "status": {
                    "type": "string",
                    "enum": [
                      "draft",
                      "active",
                      "paused",
                      "completed"
                    ]
                  },
                  "schedule": {
                    "$ref": "#/components/schemas/Schedule"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Study updated",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Study"
                }
              }
            }
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      },
      "delete": {
        "tags": [
          "Studies"
        ],
        "summary": "Delete study",
        "description": "Delete a study and all associated data (items, signals, responses, webhooks).",
        "operationId": "deleteStudy",
        "security": [
          {
            "apiKey": []
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Study deleted",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "message": {
                      "type": "string",
                      "example": "Study deleted"
                    },
                    "id": {
                      "type": "integer"
                    }
                  }
                }
              }
            }
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      }
    },
    "/v1/studies/from-template/{templateId}": {
      "post": {
        "tags": [
          "Studies"
        ],
        "summary": "Create study from template",
        "description": "Create a study from a pre-defined template. Optionally override title, description, or schedule settings.",
        "operationId": "createStudyFromTemplate",
        "security": [
          {
            "apiKey": []
          }
        ],
        "parameters": [
          {
            "name": "templateId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Template ID (e.g. daily-mood-diary, work-experience-sampling)"
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "title": {
                    "type": "string",
                    "description": "Override template title"
                  },
                  "description": {
                    "type": "string",
                    "description": "Override template description"
                  },
                  "schedule": {
                    "$ref": "#/components/schemas/Schedule"
                  }
                }
              },
              "example": {
                "title": "My Mood Study",
                "schedule": {
                  "duration_days": 21
                }
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Study created from template",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "message": {
                      "type": "string"
                    },
                    "study": {
                      "$ref": "#/components/schemas/Study"
                    },
                    "items_created": {
                      "type": "integer"
                    },
                    "template_used": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      }
    },
    "/v1/studies/{id}/deploy": {
      "post": {
        "tags": [
          "Studies"
        ],
        "summary": "Deploy study",
        "description": "Deploy a study: sets status to 'active' and generates notification signals for all enrolled participants. Requires at least one survey item.",
        "operationId": "deployStudy",
        "security": [
          {
            "apiKey": []
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Study deployed",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "message": {
                      "type": "string",
                      "example": "Study deployed successfully"
                    },
                    "status": {
                      "type": "string",
                      "example": "active"
                    },
                    "study_id": {
                      "type": "integer"
                    },
                    "signals_generated": {
                      "type": "integer",
                      "example": 350
                    },
                    "participants_count": {
                      "type": "integer",
                      "example": 5
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "No items in study",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "Add at least one survey item before deploying"
                }
              }
            }
          }
        }
      }
    },
    "/v1/studies/{id}/pause": {
      "post": {
        "tags": [
          "Studies"
        ],
        "summary": "Pause study",
        "operationId": "pauseStudy",
        "security": [
          {
            "apiKey": []
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Study paused",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "message": {
                      "type": "string",
                      "example": "Study paused"
                    },
                    "study_id": {
                      "type": "integer"
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/v1/studies/{id}/resume": {
      "post": {
        "tags": [
          "Studies"
        ],
        "summary": "Resume study",
        "operationId": "resumeStudy",
        "security": [
          {
            "apiKey": []
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Study resumed",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "message": {
                      "type": "string",
                      "example": "Study resumed"
                    },
                    "study_id": {
                      "type": "integer"
                    },
                    "status": {
                      "type": "string",
                      "example": "active"
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/v1/studies/{id}/clone": {
      "post": {
        "tags": [
          "Studies"
        ],
        "summary": "Clone study",
        "description": "Clone a study with all its items. Creates a new study in 'draft' status.",
        "operationId": "cloneStudy",
        "security": [
          {
            "apiKey": []
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "title": {
                    "type": "string",
                    "description": "Title for the cloned study"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Study cloned",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "message": {
                      "type": "string"
                    },
                    "study": {
                      "$ref": "#/components/schemas/Study"
                    },
                    "items_cloned": {
                      "type": "integer"
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/v1/studies/{id}/items": {
      "post": {
        "tags": [
          "Items"
        ],
        "summary": "Add survey items",
        "description": "Add one or more survey items to a study. Types: instruction (display-only text), likert, slider, single_choice, multi_choice, text, number. Group items on one screen with page; add screening, condition, or skip_logic for branching. To build items from an existing Word or PDF survey, use POST /v1/studies/{id}/import.",
        "operationId": "addItems",
        "security": [
          {
            "apiKey": []
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "items": {
                    "type": "array",
                    "items": {
                      "$ref": "#/components/schemas/CreateItemRequest"
                    }
                  }
                }
              },
              "example": {
                "items": [
                  {
                    "type": "single_choice",
                    "text": "Did you go to work today?",
                    "options": [
                      "Yes",
                      "No"
                    ],
                    "page": 1,
                    "screening": true,
                    "pass_values": [
                      "Yes"
                    ],
                    "fail_message": "This survey is only for work days."
                  },
                  {
                    "type": "instruction",
                    "text": "Right now, I feel...",
                    "page": 2
                  },
                  {
                    "type": "likert",
                    "text": "Happy",
                    "scale": 5,
                    "anchors": {
                      "1": "Not at all",
                      "5": "Extremely"
                    },
                    "page": 2
                  },
                  {
                    "type": "likert",
                    "text": "Tense",
                    "scale": 5,
                    "anchors": {
                      "1": "Not at all",
                      "5": "Extremely"
                    },
                    "page": 2,
                    "reverse_scored": true
                  }
                ]
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Items created",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "items": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Item"
                      }
                    },
                    "count": {
                      "type": "integer"
                    }
                  }
                }
              }
            }
          }
        }
      },
      "get": {
        "tags": [
          "Items"
        ],
        "summary": "List items",
        "description": "List all survey items for a study, ordered by sort_order.",
        "operationId": "listItems",
        "security": [
          {
            "apiKey": []
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Item list",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "items": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Item"
                      }
                    },
                    "count": {
                      "type": "integer"
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/v1/studies/{id}/items/{itemId}": {
      "delete": {
        "tags": [
          "Items"
        ],
        "summary": "Delete item",
        "operationId": "deleteItem",
        "security": [
          {
            "apiKey": []
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer"
            }
          },
          {
            "name": "itemId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Item deleted",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "message": {
                      "type": "string",
                      "example": "Item deleted"
                    },
                    "id": {
                      "type": "integer"
                    }
                  }
                }
              }
            }
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      }
    },
    "/v1/studies/{id}/participants": {
      "post": {
        "tags": [
          "Participants"
        ],
        "summary": "Enroll participants",
        "description": "Enroll participants by email. Creates participant accounts automatically if they don't exist.",
        "operationId": "enrollParticipants",
        "security": [
          {
            "apiKey": []
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "emails"
                ],
                "properties": {
                  "emails": {
                    "type": "array",
                    "items": {
                      "type": "string",
                      "format": "email"
                    },
                    "example": [
                      "alice@example.com",
                      "bob@example.com"
                    ]
                  }
                }
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Participants enrolled",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "enrolled": {
                      "type": "array",
                      "items": {
                        "type": "string"
                      }
                    },
                    "count": {
                      "type": "integer"
                    }
                  }
                }
              }
            }
          }
        }
      },
      "get": {
        "tags": [
          "Participants"
        ],
        "summary": "List participants",
        "description": "List all participants enrolled in a study.",
        "operationId": "listParticipants",
        "security": [
          {
            "apiKey": []
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Participant list",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "participants": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "id": {
                            "type": "integer"
                          },
                          "email": {
                            "type": "string"
                          },
                          "timezone": {
                            "type": "string"
                          },
                          "status": {
                            "type": "string"
                          },
                          "enrolled_at": {
                            "type": "string",
                            "format": "date-time"
                          }
                        }
                      }
                    },
                    "count": {
                      "type": "integer"
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/v1/studies/{id}/participants/bulk-invite": {
      "post": {
        "tags": [
          "Participants"
        ],
        "summary": "Bulk invite participants",
        "description": "Invite up to 500 participants at once. Optionally sends invitation emails.",
        "operationId": "bulkInvite",
        "security": [
          {
            "apiKey": []
          }
        ],
        "x-rateLimit": "20/min",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "emails"
                ],
                "properties": {
                  "emails": {
                    "type": "array",
                    "items": {
                      "type": "string",
                      "format": "email"
                    },
                    "maxItems": 500
                  },
                  "send_email": {
                    "type": "boolean",
                    "default": true
                  }
                }
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Bulk invite results",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "message": {
                      "type": "string"
                    },
                    "enrolled": {
                      "type": "array",
                      "items": {
                        "type": "string"
                      }
                    },
                    "already_enrolled": {
                      "type": "array",
                      "items": {
                        "type": "string"
                      }
                    },
                    "invalid": {
                      "type": "array",
                      "items": {
                        "type": "string"
                      }
                    },
                    "summary": {
                      "type": "object",
                      "properties": {
                        "total": {
                          "type": "integer"
                        },
                        "enrolled": {
                          "type": "integer"
                        },
                        "already_enrolled": {
                          "type": "integer"
                        },
                        "invalid": {
                          "type": "integer"
                        }
                      }
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/v1/studies/{id}/participants/{pid}": {
      "put": {
        "tags": [
          "Participants"
        ],
        "summary": "Update participant status",
        "operationId": "updateParticipantStatus",
        "security": [
          {
            "apiKey": []
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer"
            }
          },
          {
            "name": "pid",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "status"
                ],
                "properties": {
                  "status": {
                    "type": "string",
                    "enum": [
                      "active",
                      "paused",
                      "removed",
                      "completed"
                    ]
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Status updated",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "message": {
                      "type": "string"
                    },
                    "status": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/v1/studies/{id}/participants/{pid}/compliance": {
      "get": {
        "tags": [
          "Participants"
        ],
        "summary": "Participant compliance",
        "description": "Get compliance statistics for a single participant: response rate, timing, daily breakdown.",
        "operationId": "getParticipantCompliance",
        "security": [
          {
            "apiKey": []
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer"
            }
          },
          {
            "name": "pid",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Compliance data",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "participant": {
                      "type": "object"
                    },
                    "compliance": {
                      "type": "object",
                      "properties": {
                        "total_signals_sent": {
                          "type": "integer"
                        },
                        "total_responses": {
                          "type": "integer"
                        },
                        "completed_responses": {
                          "type": "integer"
                        },
                        "response_rate": {
                          "type": "number"
                        },
                        "completion_rate": {
                          "type": "number"
                        }
                      }
                    },
                    "timing": {
                      "type": "object",
                      "properties": {
                        "avg_response_latency_sec": {
                          "type": "integer",
                          "nullable": true
                        },
                        "avg_response_duration_sec": {
                          "type": "integer",
                          "nullable": true
                        }
                      }
                    },
                    "daily_last_7_days": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "date": {
                            "type": "string",
                            "format": "date"
                          },
                          "responses": {
                            "type": "integer"
                          }
                        }
                      }
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/v1/signals": {
      "get": {
        "tags": [
          "Signals"
        ],
        "summary": "List signals",
        "description": "List signals for a study with optional status filter and pagination.",
        "operationId": "listSignals",
        "security": [
          {
            "apiKey": []
          }
        ],
        "parameters": [
          {
            "name": "study_id",
            "in": "query",
            "required": true,
            "schema": {
              "type": "integer"
            }
          },
          {
            "name": "status",
            "in": "query",
            "schema": {
              "type": "string",
              "enum": [
                "pending",
                "sent",
                "expired"
              ]
            }
          },
          {
            "name": "limit",
            "in": "query",
            "schema": {
              "type": "integer",
              "default": 50
            }
          },
          {
            "name": "offset",
            "in": "query",
            "schema": {
              "type": "integer",
              "default": 0
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Signal list",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "signals": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Signal"
                      }
                    },
                    "count": {
                      "type": "integer"
                    },
                    "total": {
                      "type": "integer"
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/v1/signals/test": {
      "post": {
        "tags": [
          "Signals"
        ],
        "summary": "Send test signal",
        "description": "Create a test signal that will be sent within 1 minute.",
        "operationId": "sendTestSignal",
        "security": [
          {
            "apiKey": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "study_id",
                  "participant_id"
                ],
                "properties": {
                  "study_id": {
                    "type": "integer"
                  },
                  "participant_id": {
                    "type": "integer"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Test signal created",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "message": {
                      "type": "string"
                    },
                    "signal": {
                      "$ref": "#/components/schemas/Signal"
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/v1/signals/stats": {
      "get": {
        "tags": [
          "Signals"
        ],
        "summary": "Signal statistics",
        "description": "Get signal statistics grouped by status for a study.",
        "operationId": "signalStats",
        "security": [
          {
            "apiKey": []
          }
        ],
        "parameters": [
          {
            "name": "study_id",
            "in": "query",
            "required": true,
            "schema": {
              "type": "integer"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Signal stats",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "study_id": {
                      "type": "integer"
                    },
                    "total": {
                      "type": "integer"
                    },
                    "by_status": {
                      "type": "object",
                      "properties": {
                        "pending": {
                          "type": "integer"
                        },
                        "sent": {
                          "type": "integer"
                        },
                        "expired": {
                          "type": "integer"
                        }
                      }
                    },
                    "responded": {
                      "type": "integer"
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/v1/studies/{id}/responses": {
      "get": {
        "tags": [
          "Responses"
        ],
        "summary": "Export responses",
        "description": "Download response data in JSON or CSV format. Supports date filtering, within-person centering, and wide/long layout.",
        "operationId": "exportResponses",
        "security": [
          {
            "apiKey": []
          }
        ],
        "x-rateLimit": "10/min",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer"
            }
          },
          {
            "name": "format",
            "in": "query",
            "schema": {
              "type": "string",
              "enum": [
                "json",
                "csv"
              ],
              "default": "json"
            }
          },
          {
            "name": "layout",
            "in": "query",
            "schema": {
              "type": "string",
              "enum": [
                "long",
                "wide"
              ],
              "default": "long"
            }
          },
          {
            "name": "from",
            "in": "query",
            "schema": {
              "type": "string",
              "format": "date-time"
            },
            "description": "Start date filter"
          },
          {
            "name": "to",
            "in": "query",
            "schema": {
              "type": "string",
              "format": "date-time"
            },
            "description": "End date filter"
          },
          {
            "name": "complete_only",
            "in": "query",
            "schema": {
              "type": "string",
              "enum": [
                "true",
                "false"
              ]
            }
          },
          {
            "name": "centering",
            "in": "query",
            "schema": {
              "type": "string",
              "enum": [
                "person"
              ]
            },
            "description": "Within-person centering (adds _pm and _cwc columns)"
          },
          {
            "name": "include_metadata",
            "in": "query",
            "schema": {
              "type": "string",
              "enum": [
                "true",
                "false"
              ],
              "default": "true"
            }
          },
          {
            "name": "include_email",
            "in": "query",
            "schema": {
              "type": "string",
              "enum": [
                "true",
                "false"
              ],
              "default": "false"
            },
            "description": "Show full emails (default: masked)"
          }
        ],
        "responses": {
          "200": {
            "description": "Response data",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "study_id": {
                      "type": "integer"
                    },
                    "study_title": {
                      "type": "string"
                    },
                    "layout": {
                      "type": "string"
                    },
                    "items": {
                      "type": "array",
                      "items": {
                        "type": "object"
                      }
                    },
                    "responses": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/ResponseRow"
                      }
                    },
                    "count": {
                      "type": "integer"
                    },
                    "exported_at": {
                      "type": "string",
                      "format": "date-time"
                    }
                  }
                }
              },
              "text/csv": {
                "schema": {
                  "type": "string"
                }
              }
            }
          }
        }
      }
    },
    "/v1/studies/{id}/analytics": {
      "get": {
        "tags": [
          "Responses"
        ],
        "summary": "Study analytics",
        "description": "Comprehensive analytics: overall stats, response timing, daily trend, per-participant breakdown, and per-item descriptive statistics (mean, SD, median).",
        "operationId": "getAnalytics",
        "security": [
          {
            "apiKey": []
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer"
            }
          },
          {
            "name": "days",
            "in": "query",
            "schema": {
              "type": "integer",
              "default": 30
            },
            "description": "Days for daily trend"
          },
          {
            "name": "include_email",
            "in": "query",
            "schema": {
              "type": "string",
              "enum": [
                "true",
                "false"
              ]
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Analytics data",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Analytics"
                }
              }
            }
          }
        }
      }
    },
    "/v1/studies/{id}/webhooks": {
      "post": {
        "tags": [
          "Webhooks"
        ],
        "summary": "Create webhook",
        "description": "Register a webhook endpoint. Returns a secret for HMAC-SHA256 signature verification. Save the secret — it will not be shown again.",
        "operationId": "createWebhook",
        "security": [
          {
            "apiKey": []
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "url",
                  "events"
                ],
                "properties": {
                  "url": {
                    "type": "string",
                    "format": "uri",
                    "example": "https://your-server.com/webhook"
                  },
                  "events": {
                    "type": "array",
                    "items": {
                      "type": "string",
                      "enum": [
                        "response.submitted",
                        "participant.enrolled",
                        "study.completed",
                        "compliance.low"
                      ]
                    },
                    "example": [
                      "response.submitted",
                      "participant.enrolled"
                    ]
                  }
                }
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Webhook created",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "message": {
                      "type": "string"
                    },
                    "webhook": {
                      "$ref": "#/components/schemas/Webhook"
                    },
                    "secret": {
                      "type": "string",
                      "description": "HMAC-SHA256 secret (shown once)"
                    },
                    "signature_header": {
                      "type": "string",
                      "example": "X-PulseESM-Signature"
                    },
                    "signature_algorithm": {
                      "type": "string",
                      "example": "HMAC-SHA256"
                    }
                  }
                }
              }
            }
          }
        }
      },
      "get": {
        "tags": [
          "Webhooks"
        ],
        "summary": "List webhooks",
        "operationId": "listWebhooks",
        "security": [
          {
            "apiKey": []
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Webhook list",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "webhooks": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Webhook"
                      }
                    },
                    "count": {
                      "type": "integer"
                    },
                    "valid_events": {
                      "type": "array",
                      "items": {
                        "type": "string"
                      }
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/v1/studies/{id}/webhooks/{wid}": {
      "patch": {
        "tags": [
          "Webhooks"
        ],
        "summary": "Update webhook",
        "description": "Update webhook settings (toggle active, change events or URL).",
        "operationId": "updateWebhook",
        "security": [
          {
            "apiKey": []
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer"
            }
          },
          {
            "name": "wid",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "active": {
                    "type": "boolean"
                  },
                  "events": {
                    "type": "array",
                    "items": {
                      "type": "string",
                      "enum": [
                        "response.submitted",
                        "participant.enrolled",
                        "study.completed",
                        "compliance.low"
                      ]
                    }
                  },
                  "url": {
                    "type": "string",
                    "format": "uri"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Webhook updated",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "message": {
                      "type": "string"
                    },
                    "webhook": {
                      "$ref": "#/components/schemas/Webhook"
                    }
                  }
                }
              }
            }
          }
        }
      },
      "delete": {
        "tags": [
          "Webhooks"
        ],
        "summary": "Delete webhook",
        "operationId": "deleteWebhook",
        "security": [
          {
            "apiKey": []
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer"
            }
          },
          {
            "name": "wid",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Webhook deleted",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "message": {
                      "type": "string",
                      "example": "Webhook deleted"
                    },
                    "id": {
                      "type": "integer"
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/v1/studies/{id}/webhooks/{wid}/test": {
      "post": {
        "tags": [
          "Webhooks"
        ],
        "summary": "Test webhook",
        "description": "Send a test payload to the webhook endpoint to verify it's working.",
        "operationId": "testWebhook",
        "security": [
          {
            "apiKey": []
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer"
            }
          },
          {
            "name": "wid",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Test result",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean"
                    },
                    "status_code": {
                      "type": "integer"
                    },
                    "error": {
                      "type": "string",
                      "nullable": true
                    },
                    "payload_sent": {
                      "type": "object"
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/v1/templates": {
      "get": {
        "tags": [
          "Templates"
        ],
        "summary": "List templates",
        "description": "List all available study templates. No authentication required.",
        "operationId": "listTemplates",
        "parameters": [
          {
            "name": "category",
            "in": "query",
            "schema": {
              "type": "string",
              "enum": [
                "mental_health",
                "organizational",
                "health",
                "education"
              ]
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Template list",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "templates": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "id": {
                            "type": "string"
                          },
                          "name": {
                            "type": "string"
                          },
                          "description": {
                            "type": "string"
                          },
                          "category": {
                            "type": "string"
                          },
                          "schedule_type": {
                            "type": "string"
                          },
                          "signals_per_day": {
                            "type": "integer"
                          },
                          "duration_days": {
                            "type": "integer"
                          },
                          "item_count": {
                            "type": "integer"
                          }
                        }
                      }
                    },
                    "count": {
                      "type": "integer"
                    },
                    "categories": {
                      "type": "array",
                      "items": {
                        "type": "string"
                      }
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/v1/templates/{templateId}": {
      "get": {
        "tags": [
          "Templates"
        ],
        "summary": "Get template details",
        "description": "Get full template details including all survey items and schedule configuration.",
        "operationId": "getTemplate",
        "parameters": [
          {
            "name": "templateId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Template ID (e.g. daily-mood-diary)"
          }
        ],
        "responses": {
          "200": {
            "description": "Template details",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "template": {
                      "$ref": "#/components/schemas/Template"
                    }
                  }
                }
              }
            }
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      }
    },
    "/v1/studies/{id}/collaborators": {
      "post": {
        "tags": [
          "Collaborators"
        ],
        "summary": "Add collaborator",
        "description": "Add a collaborator to a study by email. They must already have a PulseESM account.",
        "operationId": "addCollaborator",
        "security": [
          {
            "apiKey": []
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "email"
                ],
                "properties": {
                  "email": {
                    "type": "string",
                    "format": "email"
                  },
                  "role": {
                    "type": "string",
                    "enum": [
                      "viewer",
                      "editor",
                      "admin"
                    ],
                    "default": "viewer"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Collaborator added",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "message": {
                      "type": "string"
                    },
                    "collaborator": {
                      "type": "object",
                      "properties": {
                        "id": {
                          "type": "integer"
                        },
                        "email": {
                          "type": "string"
                        },
                        "name": {
                          "type": "string"
                        },
                        "role": {
                          "type": "string"
                        }
                      }
                    }
                  }
                }
              }
            }
          }
        }
      },
      "get": {
        "tags": [
          "Collaborators"
        ],
        "summary": "List collaborators",
        "operationId": "listCollaborators",
        "security": [
          {
            "apiKey": []
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Collaborator list",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "owner": {
                      "type": "object"
                    },
                    "collaborators": {
                      "type": "array",
                      "items": {
                        "type": "object"
                      }
                    },
                    "count": {
                      "type": "integer"
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/v1/studies/{id}/collaborators/{cid}": {
      "delete": {
        "tags": [
          "Collaborators"
        ],
        "summary": "Remove collaborator",
        "operationId": "removeCollaborator",
        "security": [
          {
            "apiKey": []
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer"
            }
          },
          {
            "name": "cid",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Collaborator removed",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "message": {
                      "type": "string",
                      "example": "Collaborator removed"
                    },
                    "id": {
                      "type": "integer"
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/v1/participants/signup": {
      "post": {
        "tags": [
          "Participant API"
        ],
        "summary": "Participant signup",
        "description": "Create a participant account. Returns a JWT token (30-day expiry) for subsequent requests.",
        "operationId": "participantSignup",
        "x-rateLimit": "5/min",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "email",
                  "password"
                ],
                "properties": {
                  "email": {
                    "type": "string",
                    "format": "email"
                  },
                  "password": {
                    "type": "string",
                    "minLength": 8,
                    "maxLength": 128
                  },
                  "name": {
                    "type": "string",
                    "maxLength": 255
                  }
                }
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Account created",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "message": {
                      "type": "string"
                    },
                    "participant": {
                      "$ref": "#/components/schemas/Participant"
                    },
                    "token": {
                      "type": "string",
                      "description": "JWT token (30-day expiry)"
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/v1/participants/login": {
      "post": {
        "tags": [
          "Participant API"
        ],
        "summary": "Participant login",
        "description": "Log in as a participant. Returns JWT token (30-day expiry).",
        "operationId": "participantLogin",
        "x-rateLimit": "5/min",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "email",
                  "password"
                ],
                "properties": {
                  "email": {
                    "type": "string",
                    "format": "email"
                  },
                  "password": {
                    "type": "string"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Login successful",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "message": {
                      "type": "string"
                    },
                    "participant": {
                      "$ref": "#/components/schemas/Participant"
                    },
                    "token": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/v1/participants/me": {
      "get": {
        "tags": [
          "Participant API"
        ],
        "summary": "Get my profile",
        "operationId": "getParticipantProfile",
        "security": [
          {
            "participantJwt": []
          }
        ],
        "responses": {
          "200": {
            "description": "Profile",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "participant": {
                      "$ref": "#/components/schemas/Participant"
                    }
                  }
                }
              }
            }
          }
        }
      },
      "put": {
        "tags": [
          "Participant API"
        ],
        "summary": "Update my profile",
        "description": "Update timezone, name, and/or demographic data.",
        "operationId": "updateParticipantProfile",
        "security": [
          {
            "participantJwt": []
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "timezone": {
                    "type": "string",
                    "example": "America/New_York"
                  },
                  "name": {
                    "type": "string"
                  },
                  "demographics": {
                    "type": "object",
                    "example": {
                      "age": 25,
                      "gender": "female"
                    }
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Profile updated",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "message": {
                      "type": "string"
                    },
                    "participant": {
                      "type": "object"
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/v1/participants/password": {
      "put": {
        "tags": [
          "Participant API"
        ],
        "summary": "Change participant password",
        "operationId": "changeParticipantPassword",
        "security": [
          {
            "participantJwt": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "currentPassword",
                  "newPassword"
                ],
                "properties": {
                  "currentPassword": {
                    "type": "string"
                  },
                  "newPassword": {
                    "type": "string",
                    "minLength": 8,
                    "maxLength": 128
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Password changed",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "message": {
                      "type": "string",
                      "example": "Password changed successfully"
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/v1/participants/device-token": {
      "put": {
        "tags": [
          "Participant API"
        ],
        "summary": "Register device token",
        "description": "Register a push notification device token for the participant.",
        "operationId": "updateDeviceToken",
        "security": [
          {
            "participantJwt": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "device_token"
                ],
                "properties": {
                  "device_token": {
                    "type": "string"
                  },
                  "platform": {
                    "type": "string",
                    "enum": [
                      "ios",
                      "android",
                      "web"
                    ]
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Token updated",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "message": {
                      "type": "string",
                      "example": "Device token updated"
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/v1/participants/studies": {
      "get": {
        "tags": [
          "Participant API"
        ],
        "summary": "My studies",
        "description": "List studies the participant is enrolled in.",
        "operationId": "listParticipantStudies",
        "security": [
          {
            "participantJwt": []
          }
        ],
        "responses": {
          "200": {
            "description": "Study list",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "studies": {
                      "type": "array",
                      "items": {
                        "type": "object"
                      }
                    },
                    "count": {
                      "type": "integer"
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/v1/participants/studies/{studyId}/progress": {
      "get": {
        "tags": [
          "Participant API"
        ],
        "summary": "Study progress",
        "description": "Get detailed progress for a specific study: days elapsed/remaining, compliance rate, today's count, pending signals.",
        "operationId": "getStudyProgress",
        "security": [
          {
            "participantJwt": []
          }
        ],
        "parameters": [
          {
            "name": "studyId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Progress data",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "study": {
                      "type": "object"
                    },
                    "enrollment": {
                      "type": "object"
                    },
                    "progress": {
                      "type": "object",
                      "properties": {
                        "days_elapsed": {
                          "type": "integer"
                        },
                        "days_remaining": {
                          "type": "integer"
                        },
                        "total_days": {
                          "type": "integer"
                        },
                        "percent_complete": {
                          "type": "integer"
                        }
                      }
                    },
                    "responses": {
                      "type": "object",
                      "properties": {
                        "total_submitted": {
                          "type": "integer"
                        },
                        "signals_received": {
                          "type": "integer"
                        },
                        "compliance_rate": {
                          "type": "number"
                        },
                        "today_count": {
                          "type": "integer"
                        },
                        "pending_signals": {
                          "type": "integer"
                        }
                      }
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/v1/participants/studies/{studyId}/survey": {
      "get": {
        "tags": [
          "Participant API"
        ],
        "summary": "Get survey",
        "description": "Get the survey items for a study the participant is enrolled in.",
        "operationId": "getSurvey",
        "security": [
          {
            "participantJwt": []
          }
        ],
        "parameters": [
          {
            "name": "studyId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Survey items",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "study": {
                      "type": "object"
                    },
                    "items": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Item"
                      }
                    },
                    "count": {
                      "type": "integer"
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/v1/participants/studies/{studyId}/responses": {
      "post": {
        "tags": [
          "Participant API"
        ],
        "summary": "Submit response",
        "description": "Submit a survey response. Data keys should be item IDs. Email verification required.",
        "operationId": "submitResponse",
        "security": [
          {
            "participantJwt": []
          }
        ],
        "parameters": [
          {
            "name": "studyId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "data"
                ],
                "properties": {
                  "data": {
                    "type": "object",
                    "description": "Response data keyed by item ID",
                    "example": {
                      "1": 5,
                      "2": 80,
                      "3": "Working on my laptop"
                    }
                  },
                  "signal_id": {
                    "type": "integer",
                    "description": "The signal ID that triggered this response"
                  },
                  "response_started_at": {
                    "type": "string",
                    "format": "date-time",
                    "description": "When the participant opened the survey"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Response submitted",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "message": {
                      "type": "string",
                      "example": "Response submitted"
                    },
                    "response_id": {
                      "type": "integer"
                    },
                    "created_at": {
                      "type": "string",
                      "format": "date-time"
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/v1/participants/verify": {
      "get": {
        "tags": [
          "Participant API"
        ],
        "summary": "Verify participant email",
        "description": "Verify participant email via link. Returns an HTML page.",
        "operationId": "verifyParticipantEmail",
        "parameters": [
          {
            "name": "token",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "HTML verification page"
          }
        }
      }
    },
    "/v1/participants/resend-verification": {
      "post": {
        "tags": [
          "Participant API"
        ],
        "summary": "Resend participant verification",
        "operationId": "resendParticipantVerification",
        "x-rateLimit": "5/min",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "email"
                ],
                "properties": {
                  "email": {
                    "type": "string",
                    "format": "email"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Verification email sent",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "message": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/v1/participants/earnings": {
      "get": {
        "tags": [
          "Participant API"
        ],
        "summary": "View earnings",
        "description": "View earnings history and available balance.",
        "operationId": "getEarnings",
        "security": [
          {
            "participantJwt": []
          }
        ],
        "responses": {
          "200": {
            "description": "Earnings data",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "earnings": {
                      "type": "array",
                      "items": {
                        "type": "object"
                      }
                    },
                    "count": {
                      "type": "integer"
                    },
                    "total_earned": {
                      "type": "number"
                    },
                    "total_withdrawn": {
                      "type": "number"
                    },
                    "available_balance": {
                      "type": "number"
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/v1/participants/withdrawals": {
      "post": {
        "tags": [
          "Participant API"
        ],
        "summary": "Request withdrawal",
        "description": "Request a withdrawal of earned funds.",
        "operationId": "requestWithdrawal",
        "security": [
          {
            "participantJwt": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "amount"
                ],
                "properties": {
                  "amount": {
                    "type": "number",
                    "minimum": 0,
                    "exclusiveMinimum": true
                  },
                  "payment_method": {
                    "type": "string"
                  },
                  "payment_details": {
                    "type": "object"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Withdrawal requested",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "message": {
                      "type": "string"
                    },
                    "withdrawal": {
                      "type": "object"
                    }
                  }
                }
              }
            }
          }
        }
      },
      "get": {
        "tags": [
          "Participant API"
        ],
        "summary": "Withdrawal history",
        "operationId": "listWithdrawals",
        "security": [
          {
            "participantJwt": []
          }
        ],
        "responses": {
          "200": {
            "description": "Withdrawal list",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "withdrawals": {
                      "type": "array",
                      "items": {
                        "type": "object"
                      }
                    },
                    "count": {
                      "type": "integer"
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/v1/studies/{id}/activity": {
      "get": {
        "tags": [
          "Activity"
        ],
        "summary": "Activity log",
        "description": "Get audit trail of all actions performed on a study. Supports pagination.",
        "operationId": "getActivityLog",
        "security": [
          {
            "apiKey": []
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer"
            }
          },
          {
            "name": "limit",
            "in": "query",
            "schema": {
              "type": "integer",
              "default": 50
            }
          },
          {
            "name": "offset",
            "in": "query",
            "schema": {
              "type": "integer",
              "default": 0
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Activity log",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "activities": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "id": {
                            "type": "integer"
                          },
                          "action": {
                            "type": "string"
                          },
                          "details": {
                            "type": "object"
                          },
                          "created_at": {
                            "type": "string",
                            "format": "date-time"
                          },
                          "researcher": {
                            "type": "object",
                            "nullable": true
                          }
                        }
                      }
                    },
                    "count": {
                      "type": "integer"
                    },
                    "total": {
                      "type": "integer"
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/v1/studies/{id}/import": {
      "post": {
        "tags": [
          "Items"
        ],
        "summary": "Import survey items from a document",
        "description": "Upload a survey instrument (Word .docx, PDF, or plain text) and get back PulseESM items: question types, scales and anchors, pages grouped by lead-in, screening questions, and reverse-scored and attention-check flags.\n\nBy default this is a dry run: nothing is saved, so you can review the items and then send the ones you keep to `POST /v1/studies/{id}/items`. Send `dry_run=false` to save them directly. Limit: 20 MB per file, 20 imports per hour.\n\nExample: `curl -X POST https://pulseesm-production.up.railway.app/v1/studies/12/import -H \"Authorization: Bearer sk-...\" -F file=@survey.docx`",
        "operationId": "importItems",
        "security": [
          {
            "apiKey": []
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "multipart/form-data": {
              "schema": {
                "type": "object",
                "properties": {
                  "file": {
                    "type": "string",
                    "format": "binary",
                    "description": ".docx, .pdf, or .txt"
                  },
                  "dry_run": {
                    "type": "string",
                    "enum": [
                      "true",
                      "false"
                    ],
                    "default": "true"
                  }
                },
                "required": [
                  "file"
                ]
              }
            },
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "text": {
                    "type": "string",
                    "description": "The survey as plain text."
                  },
                  "dry_run": {
                    "type": "boolean",
                    "default": true
                  }
                },
                "required": [
                  "text"
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Dry run: parsed items (not saved)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ImportResult"
                }
              }
            }
          },
          "201": {
            "description": "Items saved (dry_run=false)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ImportResult"
                }
              }
            }
          },
          "400": {
            "description": "No file or text provided",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Study not found"
          },
          "422": {
            "description": "Unsupported file, empty document, or no questions found (code: UNSUPPORTED_FILE, EMPTY_DOCUMENT, NO_ITEMS_FOUND, IMPORT_TOO_LONG)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    }
  },
  "components": {
    "securitySchemes": {
      "apiKey": {
        "type": "http",
        "scheme": "bearer",
        "description": "Researcher API key (starts with `sk-`). Obtain via email verification or POST /v1/auth/regenerate-key."
      },
      "participantJwt": {
        "type": "http",
        "scheme": "bearer",
        "bearerFormat": "JWT",
        "description": "Participant JWT token. Obtain via POST /v1/participants/login or /signup. 30-day expiry."
      }
    },
    "schemas": {
      "Error": {
        "type": "object",
        "properties": {
          "error": {
            "type": "string",
            "description": "Human-readable error message"
          },
          "code": {
            "type": "string",
            "description": "Machine-readable error code",
            "example": "VALIDATION_ERROR"
          }
        },
        "required": [
          "error"
        ]
      },
      "Researcher": {
        "type": "object",
        "properties": {
          "id": {
            "type": "integer",
            "example": 1
          },
          "email": {
            "type": "string",
            "format": "email",
            "example": "researcher@university.edu"
          },
          "name": {
            "type": "string",
            "example": "Dr. Smith"
          },
          "plan": {
            "type": "string",
            "example": "free"
          },
          "email_verified": {
            "type": "boolean"
          }
        }
      },
      "Schedule": {
        "type": "object",
        "properties": {
          "type": {
            "type": "string",
            "enum": [
              "signal-contingent",
              "interval"
            ],
            "description": "signal-contingent: random times within window. interval: fixed intervals."
          },
          "signals_per_day": {
            "type": "integer",
            "default": 5
          },
          "window": {
            "type": "object",
            "properties": {
              "start": {
                "type": "string",
                "example": "09:00"
              },
              "end": {
                "type": "string",
                "example": "21:00"
              }
            }
          },
          "min_interval_minutes": {
            "type": "integer",
            "default": 30
          },
          "duration_days": {
            "type": "integer",
            "default": 14
          },
          "expiry_minutes": {
            "type": "integer",
            "default": 60,
            "description": "Minutes before a signal expires if not responded to"
          }
        }
      },
      "Study": {
        "type": "object",
        "properties": {
          "id": {
            "type": "integer"
          },
          "title": {
            "type": "string"
          },
          "description": {
            "type": "string",
            "nullable": true
          },
          "status": {
            "type": "string",
            "enum": [
              "draft",
              "active",
              "paused",
              "completed"
            ]
          },
          "schedule": {
            "$ref": "#/components/schemas/Schedule"
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "updated_at": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "CreateStudyRequest": {
        "type": "object",
        "required": [
          "title"
        ],
        "properties": {
          "title": {
            "type": "string",
            "maxLength": 500
          },
          "description": {
            "type": "string",
            "maxLength": 5000
          },
          "schedule": {
            "$ref": "#/components/schemas/Schedule"
          }
        }
      },
      "Item": {
        "type": "object",
        "properties": {
          "id": {
            "type": "integer"
          },
          "type": {
            "type": "string",
            "enum": [
              "instruction",
              "likert",
              "slider",
              "single_choice",
              "multi_choice",
              "text",
              "number"
            ],
            "description": "instruction = display-only text (consent reminder, a block's lead-in); never answered and never required."
          },
          "text": {
            "type": "string"
          },
          "required": {
            "type": "boolean"
          },
          "sort_order": {
            "type": "integer"
          },
          "scale": {
            "type": "integer",
            "description": "For likert type"
          },
          "anchors": {
            "type": "object",
            "additionalProperties": {
              "type": "string"
            },
            "description": "For likert: labels keyed by scale value, e.g. {\"1\": \"Strongly disagree\", \"5\": \"Strongly agree\"}"
          },
          "min": {
            "type": "number",
            "description": "For slider/number type"
          },
          "max": {
            "type": "number",
            "description": "For slider/number type"
          },
          "labels": {
            "type": "object",
            "description": "For slider type"
          },
          "options": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "For choice types"
          },
          "max_select": {
            "type": "integer",
            "description": "For multi_choice type"
          },
          "max_length": {
            "type": "integer",
            "description": "For text type"
          },
          "page": {
            "type": "integer",
            "description": "Page number. Items with the same page are shown together on one screen, in sort order. Put an instruction item first to show a block's lead-in."
          },
          "screening": {
            "type": "boolean",
            "description": "Screening question: an answer not in pass_values ends the survey and shows fail_message. By default only that survey ends (the answer is saved as a screened_out response); see screening_scope."
          },
          "pass_values": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "For screening items: the answers that let the participant continue."
          },
          "fail_message": {
            "type": "string",
            "description": "For screening items: message shown when the participant is screened out."
          },
          "condition": {
            "type": "object",
            "description": "Show this item only when another item's answer matches.",
            "properties": {
              "item_id": {
                "type": "integer"
              },
              "operator": {
                "type": "string",
                "enum": [
                  "equals",
                  "not_equals",
                  "greater_than",
                  "less_than",
                  "contains",
                  "any"
                ]
              },
              "value": {}
            }
          },
          "skip_logic": {
            "type": "array",
            "description": "Page branching: after this item's page, jump to skip_to_page when the answer matches value.",
            "items": {
              "type": "object",
              "properties": {
                "value": {
                  "oneOf": [
                    {
                      "type": "string"
                    },
                    {
                      "type": "array",
                      "items": {
                        "type": "string"
                      }
                    }
                  ]
                },
                "skip_to_page": {
                  "type": "integer",
                  "minimum": 1
                }
              },
              "required": [
                "value",
                "skip_to_page"
              ]
            }
          },
          "reverse_scored": {
            "type": "boolean",
            "description": "Metadata for analysis: the item is reverse-keyed. Not shown to participants."
          },
          "attention_check": {
            "type": "boolean",
            "description": "Metadata for analysis: the item is an attention check."
          },
          "screening_scope": {
            "type": "string",
            "enum": [
              "survey",
              "study"
            ],
            "default": "survey",
            "description": "survey (default): end only the current survey, e.g. 'Did you work today? No'. study: eligibility screener; the participant is removed from the study."
          }
        }
      },
      "CreateItemRequest": {
        "type": "object",
        "required": [
          "type",
          "text"
        ],
        "properties": {
          "type": {
            "type": "string",
            "enum": [
              "instruction",
              "likert",
              "slider",
              "single_choice",
              "multi_choice",
              "text",
              "number"
            ],
            "description": "instruction = display-only text (consent reminder, a block's lead-in); never answered and never required."
          },
          "text": {
            "type": "string",
            "maxLength": 2000
          },
          "required": {
            "type": "boolean",
            "default": true
          },
          "scale": {
            "type": "integer"
          },
          "anchors": {
            "type": "object",
            "additionalProperties": {
              "type": "string"
            },
            "description": "For likert: labels keyed by scale value, e.g. {\"1\": \"Strongly disagree\", \"5\": \"Strongly agree\"}"
          },
          "min": {
            "type": "number"
          },
          "max": {
            "type": "number"
          },
          "labels": {
            "type": "object"
          },
          "options": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "max_select": {
            "type": "integer"
          },
          "max_length": {
            "type": "integer"
          },
          "page": {
            "type": "integer",
            "description": "Page number. Items with the same page are shown together on one screen, in sort order. Put an instruction item first to show a block's lead-in."
          },
          "screening": {
            "type": "boolean",
            "description": "Screening question: an answer not in pass_values ends the survey and shows fail_message. By default only that survey ends (the answer is saved as a screened_out response); see screening_scope."
          },
          "pass_values": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "For screening items: the answers that let the participant continue."
          },
          "fail_message": {
            "type": "string",
            "description": "For screening items: message shown when the participant is screened out."
          },
          "condition": {
            "type": "object",
            "description": "Show this item only when another item's answer matches.",
            "properties": {
              "item_id": {
                "type": "integer"
              },
              "operator": {
                "type": "string",
                "enum": [
                  "equals",
                  "not_equals",
                  "greater_than",
                  "less_than",
                  "contains",
                  "any"
                ]
              },
              "value": {}
            }
          },
          "skip_logic": {
            "type": "array",
            "description": "Page branching: after this item's page, jump to skip_to_page when the answer matches value.",
            "items": {
              "type": "object",
              "properties": {
                "value": {
                  "oneOf": [
                    {
                      "type": "string"
                    },
                    {
                      "type": "array",
                      "items": {
                        "type": "string"
                      }
                    }
                  ]
                },
                "skip_to_page": {
                  "type": "integer",
                  "minimum": 1
                }
              },
              "required": [
                "value",
                "skip_to_page"
              ]
            }
          },
          "reverse_scored": {
            "type": "boolean",
            "description": "Metadata for analysis: the item is reverse-keyed. Not shown to participants."
          },
          "attention_check": {
            "type": "boolean",
            "description": "Metadata for analysis: the item is an attention check."
          },
          "screening_scope": {
            "type": "string",
            "enum": [
              "survey",
              "study"
            ],
            "default": "survey",
            "description": "survey (default): end only the current survey, e.g. 'Did you work today? No'. study: eligibility screener; the participant is removed from the study."
          }
        }
      },
      "Signal": {
        "type": "object",
        "properties": {
          "id": {
            "type": "integer"
          },
          "study_id": {
            "type": "integer"
          },
          "participant_id": {
            "type": "integer"
          },
          "participant_email": {
            "type": "string"
          },
          "scheduled_at": {
            "type": "string",
            "format": "date-time"
          },
          "sent_at": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "expires_at": {
            "type": "string",
            "format": "date-time"
          },
          "status": {
            "type": "string",
            "enum": [
              "pending",
              "sent",
              "expired"
            ]
          }
        }
      },
      "ResponseRow": {
        "type": "object",
        "properties": {
          "response_id": {
            "type": "integer"
          },
          "participant_id": {
            "type": "integer"
          },
          "participant_email": {
            "type": "string",
            "description": "Masked by default (e.g. a***@example.com)"
          },
          "signal_sent_at": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "response_started_at": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "response_completed_at": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "response_latency_sec": {
            "type": "integer",
            "description": "Seconds from signal sent to response started"
          },
          "response_duration_sec": {
            "type": "integer",
            "description": "Seconds from response started to completed"
          }
        },
        "description": "Additional item_N fields are included dynamically based on survey items"
      },
      "Webhook": {
        "type": "object",
        "properties": {
          "id": {
            "type": "integer"
          },
          "url": {
            "type": "string",
            "format": "uri"
          },
          "events": {
            "type": "array",
            "items": {
              "type": "string",
              "enum": [
                "response.submitted",
                "participant.enrolled",
                "study.completed",
                "compliance.low"
              ]
            }
          },
          "active": {
            "type": "boolean"
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "updated_at": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "Template": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "example": "daily-mood-diary"
          },
          "name": {
            "type": "string"
          },
          "description": {
            "type": "string"
          },
          "category": {
            "type": "string",
            "enum": [
              "mental_health",
              "organizational",
              "health",
              "education"
            ]
          },
          "schedule": {
            "$ref": "#/components/schemas/Schedule"
          },
          "items": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/CreateItemRequest"
            }
          }
        }
      },
      "Participant": {
        "type": "object",
        "properties": {
          "id": {
            "type": "integer"
          },
          "email": {
            "type": "string",
            "format": "email"
          },
          "name": {
            "type": "string",
            "nullable": true
          },
          "timezone": {
            "type": "string",
            "nullable": true
          },
          "demographics": {
            "type": "object"
          },
          "email_verified": {
            "type": "boolean"
          }
        }
      },
      "Analytics": {
        "type": "object",
        "properties": {
          "study_id": {
            "type": "integer"
          },
          "study_title": {
            "type": "string"
          },
          "status": {
            "type": "string"
          },
          "overall": {
            "type": "object",
            "properties": {
              "total_signals": {
                "type": "integer"
              },
              "total_responses": {
                "type": "integer"
              },
              "complete_responses": {
                "type": "integer"
              },
              "response_rate": {
                "type": "number"
              },
              "completion_rate": {
                "type": "number"
              }
            }
          },
          "timing": {
            "type": "object",
            "properties": {
              "avg_response_latency_sec": {
                "type": "integer",
                "nullable": true
              },
              "avg_response_duration_sec": {
                "type": "integer",
                "nullable": true
              },
              "avg_total_time_sec": {
                "type": "integer",
                "nullable": true
              }
            }
          },
          "daily_trend": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "date": {
                  "type": "string",
                  "format": "date"
                },
                "signals": {
                  "type": "integer"
                },
                "responses": {
                  "type": "integer"
                },
                "response_rate": {
                  "type": "number"
                }
              }
            }
          },
          "by_participant": {
            "type": "array",
            "items": {
              "type": "object"
            }
          },
          "participant_count": {
            "type": "integer"
          },
          "by_item": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "item_id": {
                  "type": "integer"
                },
                "text": {
                  "type": "string"
                },
                "type": {
                  "type": "string"
                },
                "n": {
                  "type": "integer"
                },
                "mean": {
                  "type": "number"
                },
                "sd": {
                  "type": "number"
                },
                "median": {
                  "type": "number"
                },
                "min": {
                  "type": "number"
                },
                "max": {
                  "type": "number"
                }
              }
            }
          }
        }
      },
      "ImportResult": {
        "type": "object",
        "properties": {
          "title": {
            "type": "string",
            "nullable": true,
            "description": "Survey title found in the document, if any."
          },
          "parser": {
            "type": "string",
            "enum": [
              "claude",
              "rules"
            ],
            "description": "claude = AI extraction; rules = built-in parser (used when AI is not configured)."
          },
          "warnings": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "Things the parser could not represent, such as scoring rules or time windows. Review these."
          },
          "count": {
            "type": "integer"
          },
          "dry_run": {
            "type": "boolean"
          },
          "items": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/CreateItemRequest"
            },
            "description": "With dry_run true, items ready to send to POST /v1/studies/{id}/items. With dry_run false, the created items (with ids)."
          }
        }
      }
    },
    "responses": {
      "ValidationError": {
        "description": "Validation error",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            },
            "example": {
              "error": "email, password, name are required",
              "code": "VALIDATION_ERROR"
            }
          }
        }
      },
      "AuthFailed": {
        "description": "Authentication failed",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            },
            "example": {
              "error": "Invalid email or password",
              "code": "AUTH_FAILED"
            }
          }
        }
      },
      "Unauthorized": {
        "description": "Missing or invalid API key",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            },
            "example": {
              "error": "Unauthorized",
              "code": "UNAUTHORIZED"
            }
          }
        }
      },
      "NotFound": {
        "description": "Resource not found",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            },
            "example": {
              "error": "Study not found",
              "code": "NOT_FOUND"
            }
          }
        }
      }
    }
  }
}
