{
  "openapi": "3.1.0",
  "info": {
    "title": "Thea Health Partner API",
    "version": "1.0.0",
    "summary": "Create and manage specialist eConsults from your own software.",
    "description": "The Thea Health Partner API lets EHRs, care-coordination platforms, and other\npartners create eConsults (cases), exchange messages with specialists, and\nreceive real-time updates through signed webhooks.\n\nThis reference is generated from the same OpenAPI document Thea's server is\nbuilt against. Download it at\n[docs.theahealth.com/openapi.yaml](https://docs.theahealth.com/openapi.yaml)\nto generate a client or import it into Postman.\n\n**Conventions**\n\n- JSON request and response bodies, UTF-8.\n- All timestamps are ISO 8601 in UTC, for example `2026-11-04T15:24:11Z`.\n- All IDs are opaque strings. Don't parse them or assume a format.\n- Requests about a practice's data need two keys: your partner key and\n  that practice's key. See [Authentication](https://docs.theahealth.com/concepts/authentication/).\n- Errors use [RFC 9457](https://www.rfc-editor.org/rfc/rfc9457) problem details\n  (`application/problem+json`). See [Errors](https://docs.theahealth.com/concepts/errors/).\n",
    "contact": {
      "name": "Thea Health partner support",
      "email": "support@theahealth.com",
      "url": "https://docs.theahealth.com/support/"
    }
  },
  "servers": [
    {
      "url": "https://app.theahealth.com/api/partner/v1",
      "description": "Live (requires a `thea_sk_live_` key)"
    },
    {
      "url": "https://dev.theahealth.com/api/partner/v1",
      "description": "Sandbox (requires a `thea_sk_test_` key)"
    }
  ],
  "security": [
    {
      "ApiKey": []
    }
  ],
  "tags": [
    {
      "name": "Practices",
      "description": "Practices connect to your organization once, through a Thea practice\nadmin. Each connection gives you a practice key for that practice.\n"
    },
    {
      "name": "Providers",
      "description": "Each practice's Thea roster: billable providers, staff, and admins. Seats\nare billed, so only the practice admin can add them.\n"
    },
    {
      "name": "Specialties",
      "description": "Specialties you can request an eConsult from."
    },
    {
      "name": "Patients",
      "description": "Patients belong to one practice. Thea matches a patient you send to that\npractice's existing patient by your `external_id`, then by name and date\nof birth, so you don't create duplicates. Patients in other practices are\nnever matched.\n"
    },
    {
      "name": "Cases",
      "description": "A case is an eConsult: a clinical question from a practice's provider to\none or more specialties, with attachments and a message thread.\n"
    },
    {
      "name": "Messages",
      "description": "The message thread on a case, between the practice and specialists."
    },
    {
      "name": "Files",
      "description": "Upload a file to a case in one request, and download any file on a case\nwith a short-lived URL.\n"
    },
    {
      "name": "Webhook endpoints",
      "description": "Register HTTPS endpoints to receive signed event notifications."
    }
  ],
  "paths": {
    "/connect/token": {
      "post": {
        "operationId": "exchangeConnectCode",
        "tags": [
          "Practices"
        ],
        "summary": "Complete a practice connection",
        "description": "The last step of [connecting a practice](https://docs.theahealth.com/concepts/practices/#connect-a-practice).\nAfter a practice admin approves your organization in Thea, Thea redirects\nthem to your `redirect_uri` with a `code`. Exchange it here, from your\nserver, for the practice's key and the scopes the admin approved.\n\nStore `practice_key` securely: it's returned only once. Send it as\n`Thea-Practice-Key`, with your partner key, on every request for that\npractice. It doesn't expire. Connecting the same practice again issues a\nnew key and revokes the old one, which is how you rotate it.\n\nCodes are single-use and expire after 10 minutes.\n\n**Required scopes:** none (partner key only).\n",
        "security": [
          {
            "ApiKey": []
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ConnectCodeExchange"
              },
              "examples": {
                "exchange": {
                  "value": {
                    "grant_type": "authorization_code",
                    "code": "cc_9Hd2kQ7xLm",
                    "redirect_uri": "https://app.acme-ehr.example/integrations/thea/callback"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The practice is connected.",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PracticeConnection"
                },
                "examples": {
                  "connected": {
                    "value": {
                      "practice_id": "152",
                      "practice_name": "Riverbend Family Medicine",
                      "practice_key": "thea_pk_test_7Rk2pQ9vXa3mLc8tYb4nWe6s",
                      "scopes": [
                        "patients:read",
                        "patients:write",
                        "cases:read",
                        "cases:write",
                        "messages:read",
                        "messages:write",
                        "files:write",
                        "providers:read",
                        "providers:write"
                      ],
                      "connected_at": "2026-11-04T15:04:05Z"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "The request couldn't be parsed, or the code is invalid, expired, already used, or was issued for a different `redirect_uri`.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "examples": {
                  "invalid-grant": {
                    "summary": "Code can't be used",
                    "value": {
                      "type": "https://docs.theahealth.com/concepts/errors/#invalid-grant",
                      "title": "Invalid connect code",
                      "status": 400,
                      "detail": "This code has expired. Send the admin through the connect flow again.",
                      "request_id": "req_8c41f0e2d7b9"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "409": {
            "$ref": "#/components/responses/IdempotencyConflict"
          },
          "422": {
            "$ref": "#/components/responses/ValidationFailed"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        }
      }
    },
    "/practices/{practice_id}/connection": {
      "parameters": [
        {
          "$ref": "#/components/parameters/PracticeId"
        }
      ],
      "delete": {
        "operationId": "disconnectPractice",
        "tags": [
          "Practices"
        ],
        "summary": "Disconnect a practice",
        "description": "Revokes your organization's access to the practice, and its practice key,\nimmediately. The practice admin can also disconnect you from Thea. Either\nway, Thea sends a `practice.disconnected` webhook.\n\nOpen cases carry on in Thea, and clinicians finish them there. Stop\noffering eConsults for the practice and clear its roster mapping.\n\n**Required scopes:** none (any valid practice key).\n",
        "security": [
          {
            "ApiKey": [],
            "PracticeKey": []
          }
        ],
        "responses": {
          "204": {
            "description": "The practice is disconnected.",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        }
      }
    },
    "/practices/{practice_id}/providers": {
      "parameters": [
        {
          "$ref": "#/components/parameters/PracticeId"
        }
      ],
      "get": {
        "operationId": "listPracticeProviders",
        "tags": [
          "Providers"
        ],
        "summary": "List a practice's roster",
        "description": "Returns everyone on the practice's Thea roster: billable providers, staff,\nand admins, in every status. Match your clinicians to these records (by\nNPI, then email) and keep the mapping current with the `provider.*`\nwebhooks. See [Providers and seats](https://docs.theahealth.com/concepts/providers/).\n\nOrdered by `updated_at` (oldest first) and then `id`.\n\n**Required scopes:** `providers:read`\n",
        "security": [
          {
            "ApiKey": [
              "providers:read"
            ],
            "PracticeKey": []
          }
        ],
        "parameters": [
          {
            "name": "status",
            "in": "query",
            "required": false,
            "description": "Only return roster members with this status.",
            "schema": {
              "$ref": "#/components/schemas/RosterStatus"
            }
          },
          {
            "$ref": "#/components/parameters/UpdatedSince"
          },
          {
            "$ref": "#/components/parameters/Cursor"
          },
          {
            "$ref": "#/components/parameters/Limit"
          }
        ],
        "responses": {
          "200": {
            "description": "A page of roster members.",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/RosterProviderList"
                },
                "examples": {
                  "roster": {
                    "value": {
                      "data": [
                        {
                          "id": "2210",
                          "practice_id": "152",
                          "first_name": "Sam",
                          "last_name": "Rivera",
                          "email": "sam.rivera@riverbend.example",
                          "npi": "1234567890",
                          "role": "provider",
                          "status": "active",
                          "updated_at": "2026-10-02T18:11:40Z"
                        },
                        {
                          "id": "2214",
                          "practice_id": "152",
                          "first_name": "Maria",
                          "last_name": "Alvarez",
                          "email": "ma@riverbend.example",
                          "npi": null,
                          "role": "staff",
                          "status": "active",
                          "updated_at": "2026-10-05T14:02:09Z"
                        }
                      ],
                      "has_more": false,
                      "next_cursor": null
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "422": {
            "$ref": "#/components/responses/ValidationFailed"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        }
      }
    },
    "/practices/{practice_id}/provider-requests": {
      "parameters": [
        {
          "$ref": "#/components/parameters/PracticeId"
        }
      ],
      "post": {
        "operationId": "requestProviderSeat",
        "tags": [
          "Providers"
        ],
        "summary": "Request a seat for a clinician",
        "description": "Asks the practice admin to add someone to the Thea roster. Use it when a\nclinician who isn't an `active` provider tries to send an eConsult. Seats\nare billed, so only the practice admin can approve them; the API never\ncreates or activates a seat itself.\n\nThea emails the admin. If they approve, you get a `provider.activated`\nwebhook. Until then, show the clinician a pending state and another way to\nsend the referral. Posting the same email again returns the existing\nrequest with its current `status`.\n\n**Required scopes:** `providers:write`\n",
        "security": [
          {
            "ApiKey": [
              "providers:write"
            ],
            "PracticeKey": []
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ProviderRequestCreate"
              },
              "examples": {
                "request": {
                  "value": {
                    "first_name": "Jordan",
                    "last_name": "Okafor",
                    "email": "jordan.okafor@riverbend.example",
                    "npi": "1987654321",
                    "role": "provider"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "202": {
            "description": "The request was sent to the practice admin, or already exists.",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ProviderRequest"
                },
                "examples": {
                  "pending": {
                    "value": {
                      "id": "preq_5c21",
                      "practice_id": "152",
                      "email": "jordan.okafor@riverbend.example",
                      "role": "provider",
                      "status": "pending_admin_approval",
                      "created_at": "2026-11-04T15:24:11Z"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "409": {
            "$ref": "#/components/responses/IdempotencyConflict"
          },
          "422": {
            "$ref": "#/components/responses/ValidationFailed"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        }
      }
    },
    "/specialties": {
      "get": {
        "operationId": "listSpecialties",
        "tags": [
          "Specialties"
        ],
        "summary": "List specialties",
        "description": "Returns every specialty you can request an eConsult from. Pass their `id`s\nas `specialty_ids` when you create a case. The list changes rarely; caching\nit for a day is fine.\n\n**Required scopes:** none (partner key only).\n",
        "security": [
          {
            "ApiKey": []
          }
        ],
        "responses": {
          "200": {
            "description": "All available specialties.",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SpecialtyList"
                },
                "examples": {
                  "specialties": {
                    "value": {
                      "data": [
                        {
                          "id": "12",
                          "name": "Cardiology"
                        },
                        {
                          "id": "7",
                          "name": "Dermatology"
                        },
                        {
                          "id": "21",
                          "name": "Endocrinology"
                        }
                      ],
                      "has_more": false,
                      "next_cursor": null
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        }
      }
    },
    "/patients": {
      "get": {
        "operationId": "listPatients",
        "tags": [
          "Patients"
        ],
        "summary": "List patients",
        "description": "Returns patients in the practice your practice key belongs to, ordered\nby `updated_at` (oldest first) and then `id`. Combine `updated_since` with\npagination to sync changes.\n\n**Required scopes:** `patients:read`\n",
        "security": [
          {
            "ApiKey": [
              "patients:read"
            ],
            "PracticeKey": []
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/UpdatedSince"
          },
          {
            "$ref": "#/components/parameters/Cursor"
          },
          {
            "$ref": "#/components/parameters/Limit"
          }
        ],
        "responses": {
          "200": {
            "description": "A page of patients.",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PatientList"
                },
                "examples": {
                  "page": {
                    "value": {
                      "data": [
                        {
                          "id": "18342",
                          "practice_id": "152",
                          "external_id": "acme-pt-99812",
                          "first_name": "Avery",
                          "last_name": "Johnson",
                          "date_of_birth": "1984-03-12",
                          "gender": "female",
                          "email": "avery.johnson@example.com",
                          "phone": "+15125550142",
                          "address": {
                            "line1": "410 Elm Street",
                            "city": "Austin",
                            "state": "TX",
                            "postal_code": "78701"
                          },
                          "created_at": "2026-11-04T15:24:11Z",
                          "updated_at": "2026-11-04T15:24:11Z"
                        }
                      ],
                      "has_more": true,
                      "next_cursor": "eyJ1IjoiMjAyNi0xMS0wNFQxNToyNDoxMVoiLCJpIjoiMTgzNDIifQ"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "422": {
            "$ref": "#/components/responses/ValidationFailed"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        }
      },
      "post": {
        "operationId": "upsertPatient",
        "tags": [
          "Patients"
        ],
        "summary": "Create or update a patient",
        "description": "Creates a patient, or updates the existing one if it matches. Thea looks\nonly in the practice your practice key belongs to: first for a patient with\nthe same `external_id`, then for one with the same first name, last name,\nand date of birth. Patients in other practices are never matched.\n\nReturns `201` when a patient was created and `200` when an existing one was\nupdated. Fields you omit are left unchanged on update.\n\n**Required scopes:** `patients:write`\n",
        "security": [
          {
            "ApiKey": [
              "patients:write"
            ],
            "PracticeKey": []
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/PatientUpsert"
              },
              "examples": {
                "patient": {
                  "value": {
                    "practice_id": "152",
                    "external_id": "acme-pt-99812",
                    "first_name": "Avery",
                    "last_name": "Johnson",
                    "date_of_birth": "1984-03-12",
                    "gender": "female",
                    "email": "avery.johnson@example.com",
                    "phone": "+15125550142",
                    "address": {
                      "line1": "410 Elm Street",
                      "city": "Austin",
                      "state": "TX",
                      "postal_code": "78701"
                    }
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "An existing patient matched and was updated.",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              },
              "Idempotent-Replayed": {
                "$ref": "#/components/headers/Idempotent-Replayed"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Patient"
                }
              }
            }
          },
          "201": {
            "description": "A new patient was created.",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              },
              "Idempotent-Replayed": {
                "$ref": "#/components/headers/Idempotent-Replayed"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Patient"
                },
                "examples": {
                  "created": {
                    "value": {
                      "id": "18342",
                      "practice_id": "152",
                      "external_id": "acme-pt-99812",
                      "first_name": "Avery",
                      "last_name": "Johnson",
                      "date_of_birth": "1984-03-12",
                      "gender": "female",
                      "email": "avery.johnson@example.com",
                      "phone": "+15125550142",
                      "address": {
                        "line1": "410 Elm Street",
                        "city": "Austin",
                        "state": "TX",
                        "postal_code": "78701"
                      },
                      "created_at": "2026-11-04T15:24:11Z",
                      "updated_at": "2026-11-04T15:24:11Z"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "409": {
            "$ref": "#/components/responses/IdempotencyConflict"
          },
          "422": {
            "$ref": "#/components/responses/ValidationFailed"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        }
      }
    },
    "/patients/{patient_id}": {
      "parameters": [
        {
          "$ref": "#/components/parameters/PatientId"
        }
      ],
      "get": {
        "operationId": "getPatient",
        "tags": [
          "Patients"
        ],
        "summary": "Retrieve a patient",
        "description": "Returns one patient.\n\n**Required scopes:** `patients:read`\n",
        "security": [
          {
            "ApiKey": [
              "patients:read"
            ],
            "PracticeKey": []
          }
        ],
        "responses": {
          "200": {
            "description": "The patient.",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Patient"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        }
      }
    },
    "/cases": {
      "get": {
        "operationId": "listCases",
        "tags": [
          "Cases"
        ],
        "summary": "List cases",
        "description": "Returns cases in the practice your practice key belongs to, ordered by\n`updated_at` (oldest first) and then `id`. A case's `updated_at` changes\nwhen its status, files, or messages change, or a specialist replies.\n\n**Required scopes:** `cases:read`\n",
        "security": [
          {
            "ApiKey": [
              "cases:read"
            ],
            "PracticeKey": []
          }
        ],
        "parameters": [
          {
            "name": "status",
            "in": "query",
            "required": false,
            "description": "Only return cases with this status.",
            "schema": {
              "$ref": "#/components/schemas/CaseStatus"
            }
          },
          {
            "$ref": "#/components/parameters/UpdatedSince"
          },
          {
            "$ref": "#/components/parameters/Cursor"
          },
          {
            "$ref": "#/components/parameters/Limit"
          }
        ],
        "responses": {
          "200": {
            "description": "A page of cases.",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CaseList"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "422": {
            "$ref": "#/components/responses/ValidationFailed"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        }
      },
      "post": {
        "operationId": "createCase",
        "tags": [
          "Cases"
        ],
        "summary": "Create a case",
        "description": "Submits an eConsult. Thea routes it to a specialist in the requested\nspecialty and notifies the requesting provider and specialist the same way\nas for consults created in Thea.\n\nName the clinician with `provider` (roster `id`, `npi`, or `email`). They\nmust be an `active` provider in the practice. Staff creating the case on\ntheir behalf go in `created_by_email`.\n\nIdentify the patient with `patient_id`, or send `patient` demographics to\nmatch or create one in the practice in the same request. Add files after creating the case\nwith `POST /cases/{case_id}/files`. Put your own IDs in `external_ids`; Thea echoes\nthem on the case and its webhooks. Using your referral ID as the\n`Idempotency-Key` makes retries safe.\n\n**Required scopes:** `cases:write`\n",
        "security": [
          {
            "ApiKey": [
              "cases:write"
            ],
            "PracticeKey": []
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CaseCreate"
              },
              "examples": {
                "existing-patient": {
                  "summary": "For an existing patient",
                  "value": {
                    "practice_id": "152",
                    "patient_id": "18342",
                    "provider": {
                      "npi": "1234567890"
                    },
                    "specialty_ids": [
                      "12"
                    ],
                    "question": "55F with new-onset palpitations; Holter shows frequent PVCs (8% burden). Echo normal. Is a beta-blocker reasonable first-line, and when should she see cardiology in person?",
                    "patient_consent_given": true,
                    "external_ids": {
                      "referral_id": "acme-ref-5521"
                    }
                  }
                },
                "inline-patient": {
                  "summary": "With patient demographics",
                  "value": {
                    "practice_id": "152",
                    "patient": {
                      "external_id": "acme-pt-99812",
                      "first_name": "Avery",
                      "last_name": "Johnson",
                      "date_of_birth": "1984-03-12"
                    },
                    "provider": {
                      "email": "sam.rivera@riverbend.example"
                    },
                    "created_by_email": "ma@riverbend.example",
                    "specialty_ids": [
                      "7"
                    ],
                    "question": "Pigmented lesion on left forearm, 7 mm, irregular border, photos attached. Biopsy now or monitor?",
                    "patient_consent_given": true
                  }
                }
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "The case was created.",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              },
              "Idempotent-Replayed": {
                "$ref": "#/components/headers/Idempotent-Replayed"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Case"
                },
                "examples": {
                  "created": {
                    "$ref": "#/components/examples/CaseNew"
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "409": {
            "$ref": "#/components/responses/IdempotencyConflict"
          },
          "422": {
            "description": "Some fields are invalid, patient consent wasn't confirmed, or the provider has no active seat.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ValidationProblem"
                },
                "examples": {
                  "consent-required": {
                    "summary": "Consent not confirmed",
                    "value": {
                      "type": "https://docs.theahealth.com/concepts/errors/#consent-required",
                      "title": "Patient consent required",
                      "status": 422,
                      "detail": "Set patient_consent_given to true once the patient has consented to an eConsult.",
                      "request_id": "req_8c41f0e2d7b9",
                      "errors": [
                        {
                          "field": "patient_consent_given",
                          "message": "Must be true."
                        }
                      ]
                    }
                  },
                  "provider-not-active": {
                    "summary": "Provider has no active seat",
                    "value": {
                      "type": "https://docs.theahealth.com/concepts/errors/#provider-not-active",
                      "title": "Provider not active",
                      "status": 422,
                      "detail": "Dr. Jordan Okafor has no active seat in practice 152.",
                      "request_id": "req_8c41f0e2d7b9",
                      "errors": [
                        {
                          "field": "provider",
                          "message": "Not an active provider in this practice."
                        }
                      ]
                    }
                  },
                  "unknown-provider": {
                    "summary": "Provider not on the roster",
                    "value": {
                      "type": "https://docs.theahealth.com/concepts/errors/#validation-error",
                      "title": "Validation failed",
                      "status": 422,
                      "detail": "1 field is invalid.",
                      "request_id": "req_8c41f0e2d7b9",
                      "errors": [
                        {
                          "field": "provider.npi",
                          "message": "No one with this NPI on practice 152's roster."
                        }
                      ]
                    }
                  }
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        }
      }
    },
    "/cases/{case_id}": {
      "parameters": [
        {
          "$ref": "#/components/parameters/CaseId"
        }
      ],
      "get": {
        "operationId": "getCase",
        "tags": [
          "Cases"
        ],
        "summary": "Retrieve a case",
        "description": "Returns one case, including the specialists who have replied and file metadata.\n\n**Required scopes:** `cases:read`\n",
        "security": [
          {
            "ApiKey": [
              "cases:read"
            ],
            "PracticeKey": []
          }
        ],
        "responses": {
          "200": {
            "description": "The case.",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Case"
                },
                "examples": {
                  "case": {
                    "$ref": "#/components/examples/Case"
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        }
      }
    },
    "/cases/{case_id}/close": {
      "parameters": [
        {
          "$ref": "#/components/parameters/CaseId"
        }
      ],
      "post": {
        "operationId": "closeCase",
        "tags": [
          "Cases"
        ],
        "summary": "Close a case",
        "description": "Closes the consult on behalf of an individual provider: the requesting\nprovider, or `closed_by_provider_id`. Thea records them as `closed_by`. The\nspecialist and requesting provider are notified, and the thread becomes\nread-only. Closing a closed case succeeds and changes nothing.\n\n**Required scopes:** `cases:write`\n",
        "security": [
          {
            "ApiKey": [
              "cases:write"
            ],
            "PracticeKey": []
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "requestBody": {
          "required": false,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "closed_by_provider_id": {
                    "type": "string",
                    "description": "Optional. The roster `id` of the `active` provider closing the\ncase. Defaults to the case's requesting provider.\n",
                    "examples": [
                      "2210"
                    ]
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The closed case.",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Case"
                },
                "examples": {
                  "case": {
                    "$ref": "#/components/examples/CaseClosed"
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "$ref": "#/components/responses/IdempotencyConflict"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        }
      }
    },
    "/cases/{case_id}/reopen": {
      "parameters": [
        {
          "$ref": "#/components/parameters/CaseId"
        }
      ],
      "post": {
        "operationId": "reopenCase",
        "tags": [
          "Cases"
        ],
        "summary": "Reopen a case",
        "description": "Reopens a closed consult so the thread can continue, on behalf of the\nrequesting provider or `reopened_by_provider_id`. Reopening an open case\nsucceeds and changes nothing.\n\n**Required scopes:** `cases:write`\n",
        "security": [
          {
            "ApiKey": [
              "cases:write"
            ],
            "PracticeKey": []
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "requestBody": {
          "required": false,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "reopened_by_provider_id": {
                    "type": "string",
                    "description": "Optional. The roster `id` of the `active` provider reopening the\ncase. Defaults to the case's requesting provider.\n",
                    "examples": [
                      "2210"
                    ]
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The reopened case.",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Case"
                },
                "examples": {
                  "case": {
                    "$ref": "#/components/examples/Case"
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "$ref": "#/components/responses/IdempotencyConflict"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        }
      }
    },
    "/cases/{case_id}/files": {
      "parameters": [
        {
          "$ref": "#/components/parameters/CaseId"
        }
      ],
      "post": {
        "operationId": "uploadCaseFile",
        "tags": [
          "Files"
        ],
        "summary": "Upload a file to a case",
        "description": "Uploads a file and attaches it to the case in one request, as\n`multipart/form-data`. The file is posted in the case thread from the\ncase's requesting provider, or from `author_provider_id` if you set it,\nand specialists see it immediately, like a file attached in Thea.\n\nPDF, JPEG, PNG, and HEIC files up to 25 MB are accepted. Send one file per\nrequest. You can't upload to a closed case; reopen it first.\n\n**Required scopes:** `cases:write`, `files:write`\n",
        "security": [
          {
            "ApiKey": [
              "cases:write",
              "files:write"
            ],
            "PracticeKey": []
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "multipart/form-data": {
              "schema": {
                "type": "object",
                "required": [
                  "file"
                ],
                "properties": {
                  "file": {
                    "type": "string",
                    "format": "binary",
                    "description": "The file, with its filename and content type in the part's headers."
                  },
                  "author_provider_id": {
                    "type": "string",
                    "description": "Optional. The roster `id` of the `active` provider or staff\nmember posting the file. Defaults to the case's requesting\nprovider.\n"
                  }
                }
              },
              "encoding": {
                "file": {
                  "contentType": "application/pdf, image/jpeg, image/png, image/heic"
                }
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "The file was uploaded and attached to the case.",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              },
              "Idempotent-Replayed": {
                "$ref": "#/components/headers/Idempotent-Replayed"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/File"
                },
                "examples": {
                  "uploaded": {
                    "value": {
                      "id": "6d0e4f2a9b",
                      "filename": "ecg-2026-11-02.pdf",
                      "content_type": "application/pdf",
                      "size_bytes": 482113
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "description": "The case is closed, or the `Idempotency-Key` was reused with a different request.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "examples": {
                  "case-closed": {
                    "summary": "Case is closed",
                    "value": {
                      "type": "https://docs.theahealth.com/concepts/errors/#case-closed",
                      "title": "Case is closed",
                      "status": 409,
                      "detail": "Reopen case a1f3c9e27b4d4e0f9c6b2d8e5f7a1c3b before uploading files.",
                      "request_id": "req_8c41f0e2d7b9"
                    }
                  }
                }
              }
            }
          },
          "413": {
            "description": "The file is larger than 25 MB.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "examples": {
                  "file-too-large": {
                    "value": {
                      "type": "https://docs.theahealth.com/concepts/errors/#file-too-large",
                      "title": "File too large",
                      "status": 413,
                      "detail": "Files can be up to 25 MB. This one is 31.2 MB.",
                      "request_id": "req_8c41f0e2d7b9"
                    }
                  }
                }
              }
            }
          },
          "422": {
            "$ref": "#/components/responses/ValidationFailed"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        }
      }
    },
    "/cases/{case_id}/messages": {
      "parameters": [
        {
          "$ref": "#/components/parameters/CaseId"
        }
      ],
      "get": {
        "operationId": "listCaseMessages",
        "tags": [
          "Messages"
        ],
        "summary": "List messages on a case",
        "description": "Returns the case's message thread, oldest first.\n\n**Required scopes:** `messages:read`\n",
        "security": [
          {
            "ApiKey": [
              "messages:read"
            ],
            "PracticeKey": []
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/Cursor"
          },
          {
            "$ref": "#/components/parameters/Limit"
          }
        ],
        "responses": {
          "200": {
            "description": "A page of messages.",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/MessageList"
                },
                "examples": {
                  "thread": {
                    "value": {
                      "data": [
                        {
                          "id": "88101",
                          "case_id": "a1f3c9e27b4d4e0f9c6b2d8e5f7a1c3b",
                          "body": "Reasonable to start metoprolol succinate 25 mg daily. Repeat Holter in 3 months; refer in person if burden stays above 10% or she develops symptoms of LV dysfunction.",
                          "sender": {
                            "type": "specialist",
                            "id": "3051",
                            "name": "Dr. Priya Shah",
                            "via": null
                          },
                          "files": [],
                          "created_at": "2026-11-04T16:02:45Z"
                        },
                        {
                          "id": "88123",
                          "case_id": "a1f3c9e27b4d4e0f9c6b2d8e5f7a1c3b",
                          "body": "Thanks. Her TSH came back normal at 1.9 if that changes anything.",
                          "sender": {
                            "type": "provider",
                            "id": "2210",
                            "name": "Dr. Sam Rivera",
                            "via": "Acme EHR"
                          },
                          "files": [],
                          "created_at": "2026-11-04T16:10:02Z"
                        }
                      ],
                      "has_more": false,
                      "next_cursor": null
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        }
      },
      "post": {
        "operationId": "createCaseMessage",
        "tags": [
          "Messages"
        ],
        "summary": "Post a message",
        "description": "Adds a message to the case thread. It's posted as the case's requesting\nprovider unless you set `author_provider_id` to another `active` provider\nor staff member in the practice. Specialists see it live, marked \"via\" your\npartner name, and get the usual notification. To share a file, upload it\nwith `POST /cases/{case_id}/files`. You can't post to a closed case; reopen\nit first.\n\nThe thread is one chronological conversation per case. There's no\nreply-to or nested threading.\n\n**Required scopes:** `messages:write`\n",
        "security": [
          {
            "ApiKey": [
              "messages:write"
            ],
            "PracticeKey": []
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/MessageCreate"
              },
              "examples": {
                "message": {
                  "value": {
                    "body": "Thanks. Her TSH came back normal at 1.9 if that changes anything."
                  }
                },
                "from-staff": {
                  "summary": "From staff",
                  "value": {
                    "body": "Following up for Dr. Rivera. Her TSH came back normal at 1.9.",
                    "author_provider_id": "2214"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "The message was posted.",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              },
              "Idempotent-Replayed": {
                "$ref": "#/components/headers/Idempotent-Replayed"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Message"
                },
                "examples": {
                  "message": {
                    "$ref": "#/components/examples/Message"
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "description": "The case is closed, or the `Idempotency-Key` was reused with a different request.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "examples": {
                  "case-closed": {
                    "summary": "Case is closed",
                    "value": {
                      "type": "https://docs.theahealth.com/concepts/errors/#case-closed",
                      "title": "Case is closed",
                      "status": 409,
                      "detail": "Reopen case a1f3c9e27b4d4e0f9c6b2d8e5f7a1c3b before posting messages.",
                      "request_id": "req_8c41f0e2d7b9"
                    }
                  },
                  "idempotency-key-reused": {
                    "summary": "Idempotency key reused",
                    "value": {
                      "type": "https://docs.theahealth.com/concepts/errors/#idempotency-key-reused",
                      "title": "Idempotency key reused",
                      "status": 409,
                      "detail": "This Idempotency-Key was already used with a different request body.",
                      "request_id": "req_8c41f0e2d7b9"
                    }
                  }
                }
              }
            }
          },
          "422": {
            "$ref": "#/components/responses/ValidationFailed"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        }
      }
    },
    "/files/{file_id}": {
      "parameters": [
        {
          "$ref": "#/components/parameters/FileId"
        }
      ],
      "get": {
        "operationId": "getFile",
        "tags": [
          "Files"
        ],
        "summary": "Retrieve a file",
        "description": "Returns a file's metadata and a download URL that works for 5 minutes.\nWorks for any file on a case in the practice, including files that\nspecialists and providers attached in Thea.\n\n**Required scopes:** `cases:read`\n",
        "security": [
          {
            "ApiKey": [
              "cases:read"
            ],
            "PracticeKey": []
          }
        ],
        "responses": {
          "200": {
            "description": "The file, with a short-lived download URL.",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/FileWithDownload"
                },
                "examples": {
                  "file": {
                    "value": {
                      "id": "6d0e4f2a9b",
                      "filename": "ecg-2026-11-02.pdf",
                      "content_type": "application/pdf",
                      "size_bytes": 482113,
                      "download_url": "https://s3.us-east-2.amazonaws.com/example-bucket/6d0e4f2a9b?X-Amz-Expires=300&X-Amz-Signature=8b1f0c4e",
                      "download_url_expires_at": "2026-11-04T15:29:11Z"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        }
      }
    },
    "/webhook-endpoints": {
      "get": {
        "operationId": "listWebhookEndpoints",
        "tags": [
          "Webhook endpoints"
        ],
        "summary": "List webhook endpoints",
        "description": "Returns your organization's webhook endpoints. Secrets aren't included.\n\n**Required scopes:** none (partner key only). Webhook endpoints belong\nto your organization, not to a practice.\n",
        "security": [
          {
            "ApiKey": []
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/Cursor"
          },
          {
            "$ref": "#/components/parameters/Limit"
          }
        ],
        "responses": {
          "200": {
            "description": "A page of webhook endpoints.",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/WebhookEndpointList"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        }
      },
      "post": {
        "operationId": "createWebhookEndpoint",
        "tags": [
          "Webhook endpoints"
        ],
        "summary": "Create a webhook endpoint",
        "description": "Registers an HTTPS URL to receive the events you choose, for every practice\nconnected to your organization. The response includes the endpoint's signing\n`secret`. **This is the only time it's returned**, so store it now.\n\nSandbox (`test`) keys can only register sandbox endpoints, and live keys\nonly live endpoints.\n\n**Required scopes:** none (partner key only). Webhook endpoints belong\nto your organization, not to a practice.\n",
        "security": [
          {
            "ApiKey": []
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/WebhookEndpointCreate"
              },
              "examples": {
                "endpoint": {
                  "value": {
                    "url": "https://hooks.acme-ehr.example/thea",
                    "events": [
                      "case.message.created",
                      "case.closed"
                    ],
                    "description": "Production consult sync"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "The endpoint was created. Store `secret` now.",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              },
              "Idempotent-Replayed": {
                "$ref": "#/components/headers/Idempotent-Replayed"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/WebhookEndpointWithSecret"
                },
                "examples": {
                  "created": {
                    "value": {
                      "id": "17",
                      "url": "https://hooks.acme-ehr.example/thea",
                      "description": "Production consult sync",
                      "events": [
                        "case.message.created",
                        "case.closed"
                      ],
                      "active": true,
                      "created_at": "2026-11-04T15:24:11Z",
                      "secret": "thea_whsec_9fK2mQ7vR1xL0pT4sW8yB3nD6hJ5gC"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "409": {
            "$ref": "#/components/responses/IdempotencyConflict"
          },
          "422": {
            "$ref": "#/components/responses/ValidationFailed"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        }
      }
    },
    "/webhook-endpoints/{webhook_endpoint_id}": {
      "parameters": [
        {
          "$ref": "#/components/parameters/WebhookEndpointId"
        }
      ],
      "get": {
        "operationId": "getWebhookEndpoint",
        "tags": [
          "Webhook endpoints"
        ],
        "summary": "Retrieve a webhook endpoint",
        "description": "Returns one webhook endpoint. The secret isn't included.\n\n**Required scopes:** none (partner key only). Webhook endpoints belong\nto your organization, not to a practice.\n",
        "security": [
          {
            "ApiKey": []
          }
        ],
        "responses": {
          "200": {
            "description": "The webhook endpoint.",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/WebhookEndpoint"
                },
                "examples": {
                  "endpoint": {
                    "$ref": "#/components/examples/WebhookEndpoint"
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        }
      },
      "delete": {
        "operationId": "deleteWebhookEndpoint",
        "tags": [
          "Webhook endpoints"
        ],
        "summary": "Delete a webhook endpoint",
        "description": "Stops all deliveries to the endpoint, including pending retries. This can't be undone.\n\n**Required scopes:** none (partner key only). Webhook endpoints belong\nto your organization, not to a practice.\n",
        "security": [
          {
            "ApiKey": []
          }
        ],
        "responses": {
          "204": {
            "description": "The endpoint was deleted.",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        }
      }
    },
    "/webhook-endpoints/{webhook_endpoint_id}/test": {
      "parameters": [
        {
          "$ref": "#/components/parameters/WebhookEndpointId"
        }
      ],
      "post": {
        "operationId": "testWebhookEndpoint",
        "tags": [
          "Webhook endpoints"
        ],
        "summary": "Send a test event",
        "description": "Sends a signed `webhook.ping` event to the endpoint right away and reports\nwhat happened. Use it to check your signature verification before real\nevents arrive. Test events aren't retried.\n\nThe test event's `data` is `{ \"webhook_endpoint_id\": \"<id>\" }`.\n\n**Required scopes:** none (partner key only). Webhook endpoints belong\nto your organization, not to a practice.\n",
        "security": [
          {
            "ApiKey": []
          }
        ],
        "responses": {
          "200": {
            "description": "The delivery attempt's result. Check `delivered`.",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/WebhookTestResult"
                },
                "examples": {
                  "delivered": {
                    "summary": "Delivered",
                    "value": {
                      "event_id": "50212",
                      "delivered": true,
                      "status_code": 200,
                      "error": null
                    }
                  },
                  "timeout": {
                    "summary": "Endpoint didn't respond",
                    "value": {
                      "event_id": "50213",
                      "delivered": false,
                      "status_code": null,
                      "error": "timeout"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        }
      }
    }
  },
  "webhooks": {
    "case.created": {
      "post": {
        "operationId": "case_created_event",
        "summary": "Case created",
        "description": "A case was created in one of your practices, in Thea or through the API.\n\nDelivered as a signed `POST` to every active endpoint subscribed to\n`case.created`. Respond with any 2xx status within 5 seconds.\n",
        "security": [],
        "parameters": [
          {
            "in": "header",
            "name": "Thea-Signature",
            "required": true,
            "description": "See [Verify signatures](https://docs.theahealth.com/guides/webhooks/#verify-signatures).",
            "schema": {
              "type": "string"
            }
          },
          {
            "in": "header",
            "name": "Thea-Event-Id",
            "required": true,
            "description": "The event's `id`, repeated as a header.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "allOf": [
                  {
                    "$ref": "#/components/schemas/Event"
                  },
                  {
                    "type": "object",
                    "properties": {
                      "type": {
                        "const": "case.created"
                      },
                      "data": {
                        "type": "object",
                        "required": [
                          "case_id",
                          "external_ids",
                          "patient_id",
                          "specialty_ids"
                        ],
                        "properties": {
                          "external_ids": {
                            "$ref": "#/components/schemas/ExternalIds"
                          },
                          "case_id": {
                            "type": "string",
                            "description": "The new case."
                          },
                          "patient_id": {
                            "type": "string"
                          },
                          "specialty_ids": {
                            "type": "array",
                            "items": {
                              "type": "string"
                            }
                          }
                        }
                      }
                    }
                  }
                ]
              },
              "examples": {
                "event": {
                  "value": {
                    "id": "50201",
                    "type": "case.created",
                    "created_at": "2026-11-04T16:02:45Z",
                    "practice_id": "152",
                    "data": {
                      "case_id": "a1f3c9e27b4d4e0f9c6b2d8e5f7a1c3b",
                      "external_ids": {
                        "referral_id": "acme-ref-5521"
                      },
                      "patient_id": "18342",
                      "specialty_ids": [
                        "12"
                      ]
                    }
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Return any 2xx status to acknowledge the event. Anything else, or no response within 5 seconds, is retried."
          }
        }
      }
    },
    "case.closed": {
      "post": {
        "operationId": "case_closed_event",
        "summary": "Case closed",
        "description": "A case was closed.\n\nDelivered as a signed `POST` to every active endpoint subscribed to\n`case.closed`. Respond with any 2xx status within 5 seconds.\n",
        "security": [],
        "parameters": [
          {
            "in": "header",
            "name": "Thea-Signature",
            "required": true,
            "description": "See [Verify signatures](https://docs.theahealth.com/guides/webhooks/#verify-signatures).",
            "schema": {
              "type": "string"
            }
          },
          {
            "in": "header",
            "name": "Thea-Event-Id",
            "required": true,
            "description": "The event's `id`, repeated as a header.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "allOf": [
                  {
                    "$ref": "#/components/schemas/Event"
                  },
                  {
                    "type": "object",
                    "properties": {
                      "type": {
                        "const": "case.closed"
                      },
                      "data": {
                        "type": "object",
                        "required": [
                          "case_id",
                          "external_ids",
                          "closed_at"
                        ],
                        "properties": {
                          "external_ids": {
                            "$ref": "#/components/schemas/ExternalIds"
                          },
                          "case_id": {
                            "type": "string"
                          },
                          "closed_at": {
                            "type": "string",
                            "format": "date-time"
                          }
                        }
                      }
                    }
                  }
                ]
              },
              "examples": {
                "event": {
                  "value": {
                    "id": "50211",
                    "type": "case.closed",
                    "created_at": "2026-11-04T17:30:00Z",
                    "practice_id": "152",
                    "data": {
                      "case_id": "a1f3c9e27b4d4e0f9c6b2d8e5f7a1c3b",
                      "external_ids": {
                        "referral_id": "acme-ref-5521"
                      },
                      "closed_at": "2026-11-04T17:30:00Z"
                    }
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Return any 2xx status to acknowledge the event. Anything else, or no response within 5 seconds, is retried."
          }
        }
      }
    },
    "case.reopened": {
      "post": {
        "operationId": "case_reopened_event",
        "summary": "Case reopened",
        "description": "A closed case was reopened.\n\nDelivered as a signed `POST` to every active endpoint subscribed to\n`case.reopened`. Respond with any 2xx status within 5 seconds.\n",
        "security": [],
        "parameters": [
          {
            "in": "header",
            "name": "Thea-Signature",
            "required": true,
            "description": "See [Verify signatures](https://docs.theahealth.com/guides/webhooks/#verify-signatures).",
            "schema": {
              "type": "string"
            }
          },
          {
            "in": "header",
            "name": "Thea-Event-Id",
            "required": true,
            "description": "The event's `id`, repeated as a header.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "allOf": [
                  {
                    "$ref": "#/components/schemas/Event"
                  },
                  {
                    "type": "object",
                    "properties": {
                      "type": {
                        "const": "case.reopened"
                      },
                      "data": {
                        "type": "object",
                        "required": [
                          "case_id",
                          "external_ids"
                        ],
                        "properties": {
                          "external_ids": {
                            "$ref": "#/components/schemas/ExternalIds"
                          },
                          "case_id": {
                            "type": "string"
                          }
                        }
                      }
                    }
                  }
                ]
              },
              "examples": {
                "event": {
                  "value": {
                    "id": "50219",
                    "type": "case.reopened",
                    "created_at": "2026-11-04T16:02:45Z",
                    "practice_id": "152",
                    "data": {
                      "case_id": "a1f3c9e27b4d4e0f9c6b2d8e5f7a1c3b",
                      "external_ids": {
                        "referral_id": "acme-ref-5521"
                      }
                    }
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Return any 2xx status to acknowledge the event. Anything else, or no response within 5 seconds, is retried."
          }
        }
      }
    },
    "case.message.created": {
      "post": {
        "operationId": "case_message_created_event",
        "summary": "Message posted",
        "description": "A message was posted on a case, by anyone, including through the API. Fetch it with `GET /cases/{case_id}/messages`.\n\nDelivered as a signed `POST` to every active endpoint subscribed to\n`case.message.created`. Respond with any 2xx status within 5 seconds.\n",
        "security": [],
        "parameters": [
          {
            "in": "header",
            "name": "Thea-Signature",
            "required": true,
            "description": "See [Verify signatures](https://docs.theahealth.com/guides/webhooks/#verify-signatures).",
            "schema": {
              "type": "string"
            }
          },
          {
            "in": "header",
            "name": "Thea-Event-Id",
            "required": true,
            "description": "The event's `id`, repeated as a header.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "allOf": [
                  {
                    "$ref": "#/components/schemas/Event"
                  },
                  {
                    "type": "object",
                    "properties": {
                      "type": {
                        "const": "case.message.created"
                      },
                      "data": {
                        "type": "object",
                        "required": [
                          "case_id",
                          "external_ids",
                          "message_id",
                          "sender_type"
                        ],
                        "properties": {
                          "external_ids": {
                            "$ref": "#/components/schemas/ExternalIds"
                          },
                          "case_id": {
                            "type": "string"
                          },
                          "message_id": {
                            "type": "string"
                          },
                          "sender_type": {
                            "type": "string",
                            "enum": [
                              "provider",
                              "staff",
                              "specialist",
                              "support"
                            ]
                          }
                        }
                      }
                    }
                  }
                ]
              },
              "examples": {
                "event": {
                  "value": {
                    "id": "50207",
                    "type": "case.message.created",
                    "created_at": "2026-11-04T16:02:45Z",
                    "practice_id": "152",
                    "data": {
                      "case_id": "a1f3c9e27b4d4e0f9c6b2d8e5f7a1c3b",
                      "external_ids": {
                        "referral_id": "acme-ref-5521"
                      },
                      "message_id": "88101",
                      "sender_type": "specialist"
                    }
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Return any 2xx status to acknowledge the event. Anything else, or no response within 5 seconds, is retried."
          }
        }
      }
    },
    "patient.created": {
      "post": {
        "operationId": "patient_created_event",
        "summary": "Patient created",
        "description": "A patient was created in one of your practices.\n\nDelivered as a signed `POST` to every active endpoint subscribed to\n`patient.created`. Respond with any 2xx status within 5 seconds.\n",
        "security": [],
        "parameters": [
          {
            "in": "header",
            "name": "Thea-Signature",
            "required": true,
            "description": "See [Verify signatures](https://docs.theahealth.com/guides/webhooks/#verify-signatures).",
            "schema": {
              "type": "string"
            }
          },
          {
            "in": "header",
            "name": "Thea-Event-Id",
            "required": true,
            "description": "The event's `id`, repeated as a header.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "allOf": [
                  {
                    "$ref": "#/components/schemas/Event"
                  },
                  {
                    "type": "object",
                    "properties": {
                      "type": {
                        "const": "patient.created"
                      },
                      "data": {
                        "type": "object",
                        "required": [
                          "patient_id"
                        ],
                        "properties": {
                          "patient_id": {
                            "type": "string"
                          }
                        }
                      }
                    }
                  }
                ]
              },
              "examples": {
                "event": {
                  "value": {
                    "id": "50199",
                    "type": "patient.created",
                    "created_at": "2026-11-04T16:02:45Z",
                    "practice_id": "152",
                    "data": {
                      "patient_id": "18342"
                    }
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Return any 2xx status to acknowledge the event. Anything else, or no response within 5 seconds, is retried."
          }
        }
      }
    },
    "patient.updated": {
      "post": {
        "operationId": "patient_updated_event",
        "summary": "Patient updated",
        "description": "A patient's demographics changed.\n\nDelivered as a signed `POST` to every active endpoint subscribed to\n`patient.updated`. Respond with any 2xx status within 5 seconds.\n",
        "security": [],
        "parameters": [
          {
            "in": "header",
            "name": "Thea-Signature",
            "required": true,
            "description": "See [Verify signatures](https://docs.theahealth.com/guides/webhooks/#verify-signatures).",
            "schema": {
              "type": "string"
            }
          },
          {
            "in": "header",
            "name": "Thea-Event-Id",
            "required": true,
            "description": "The event's `id`, repeated as a header.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "allOf": [
                  {
                    "$ref": "#/components/schemas/Event"
                  },
                  {
                    "type": "object",
                    "properties": {
                      "type": {
                        "const": "patient.updated"
                      },
                      "data": {
                        "type": "object",
                        "required": [
                          "patient_id"
                        ],
                        "properties": {
                          "patient_id": {
                            "type": "string"
                          }
                        }
                      }
                    }
                  }
                ]
              },
              "examples": {
                "event": {
                  "value": {
                    "id": "50230",
                    "type": "patient.updated",
                    "created_at": "2026-11-04T16:02:45Z",
                    "practice_id": "152",
                    "data": {
                      "patient_id": "18342"
                    }
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Return any 2xx status to acknowledge the event. Anything else, or no response within 5 seconds, is retried."
          }
        }
      }
    },
    "provider.activated": {
      "post": {
        "operationId": "provider_activated_event",
        "summary": "Provider activated",
        "description": "Someone on a connected practice's roster became `active`: the practice\nadmin added them or approved a seat request.\n\nDelivered as a signed `POST` to every active endpoint subscribed to\n`provider.activated`. Respond with any 2xx status within 5 seconds.\n",
        "security": [],
        "parameters": [
          {
            "in": "header",
            "name": "Thea-Signature",
            "required": true,
            "description": "See [Verify signatures](https://docs.theahealth.com/guides/webhooks/#verify-signatures).",
            "schema": {
              "type": "string"
            }
          },
          {
            "in": "header",
            "name": "Thea-Event-Id",
            "required": true,
            "description": "The event's `id`, repeated as a header.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "allOf": [
                  {
                    "$ref": "#/components/schemas/Event"
                  },
                  {
                    "type": "object",
                    "properties": {
                      "type": {
                        "const": "provider.activated"
                      },
                      "data": {
                        "type": "object",
                        "required": [
                          "provider_id",
                          "role",
                          "status"
                        ],
                        "properties": {
                          "provider_id": {
                            "type": "string",
                            "description": "The roster `id`. Fetch the roster for names, email, and NPI."
                          },
                          "role": {
                            "$ref": "#/components/schemas/RosterRole"
                          },
                          "status": {
                            "$ref": "#/components/schemas/RosterStatus"
                          }
                        }
                      }
                    }
                  }
                ]
              },
              "examples": {
                "event": {
                  "value": {
                    "id": "50301",
                    "type": "provider.activated",
                    "created_at": "2026-11-04T18:05:12Z",
                    "practice_id": "152",
                    "data": {
                      "provider_id": "2231",
                      "role": "provider",
                      "status": "active"
                    }
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Return any 2xx status to acknowledge the event. Anything else, or no response within 5 seconds, is retried."
          }
        }
      }
    },
    "provider.deactivated": {
      "post": {
        "operationId": "provider_deactivated_event",
        "summary": "Provider deactivated",
        "description": "Someone on a connected practice's roster became `inactive`. They can no\nlonger send cases or post messages. Their existing cases stay in Thea.\n\nDelivered as a signed `POST` to every active endpoint subscribed to\n`provider.deactivated`. Respond with any 2xx status within 5 seconds.\n",
        "security": [],
        "parameters": [
          {
            "in": "header",
            "name": "Thea-Signature",
            "required": true,
            "description": "See [Verify signatures](https://docs.theahealth.com/guides/webhooks/#verify-signatures).",
            "schema": {
              "type": "string"
            }
          },
          {
            "in": "header",
            "name": "Thea-Event-Id",
            "required": true,
            "description": "The event's `id`, repeated as a header.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "allOf": [
                  {
                    "$ref": "#/components/schemas/Event"
                  },
                  {
                    "type": "object",
                    "properties": {
                      "type": {
                        "const": "provider.deactivated"
                      },
                      "data": {
                        "type": "object",
                        "required": [
                          "provider_id",
                          "role",
                          "status"
                        ],
                        "properties": {
                          "provider_id": {
                            "type": "string",
                            "description": "The roster `id`. Fetch the roster for names, email, and NPI."
                          },
                          "role": {
                            "$ref": "#/components/schemas/RosterRole"
                          },
                          "status": {
                            "$ref": "#/components/schemas/RosterStatus"
                          }
                        }
                      }
                    }
                  }
                ]
              },
              "examples": {
                "event": {
                  "value": {
                    "id": "50302",
                    "type": "provider.deactivated",
                    "created_at": "2026-11-04T18:05:12Z",
                    "practice_id": "152",
                    "data": {
                      "provider_id": "2231",
                      "role": "provider",
                      "status": "inactive"
                    }
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Return any 2xx status to acknowledge the event. Anything else, or no response within 5 seconds, is retried."
          }
        }
      }
    },
    "provider.updated": {
      "post": {
        "operationId": "provider_updated_event",
        "summary": "Provider updated",
        "description": "A roster member's name, email, NPI, or role changed. Fetch the roster to\nupdate your mapping.\n\nDelivered as a signed `POST` to every active endpoint subscribed to\n`provider.updated`. Respond with any 2xx status within 5 seconds.\n",
        "security": [],
        "parameters": [
          {
            "in": "header",
            "name": "Thea-Signature",
            "required": true,
            "description": "See [Verify signatures](https://docs.theahealth.com/guides/webhooks/#verify-signatures).",
            "schema": {
              "type": "string"
            }
          },
          {
            "in": "header",
            "name": "Thea-Event-Id",
            "required": true,
            "description": "The event's `id`, repeated as a header.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "allOf": [
                  {
                    "$ref": "#/components/schemas/Event"
                  },
                  {
                    "type": "object",
                    "properties": {
                      "type": {
                        "const": "provider.updated"
                      },
                      "data": {
                        "type": "object",
                        "required": [
                          "provider_id",
                          "role",
                          "status"
                        ],
                        "properties": {
                          "provider_id": {
                            "type": "string",
                            "description": "The roster `id`. Fetch the roster for names, email, and NPI."
                          },
                          "role": {
                            "$ref": "#/components/schemas/RosterRole"
                          },
                          "status": {
                            "$ref": "#/components/schemas/RosterStatus"
                          }
                        }
                      }
                    }
                  }
                ]
              },
              "examples": {
                "event": {
                  "value": {
                    "id": "50303",
                    "type": "provider.updated",
                    "created_at": "2026-11-04T18:05:12Z",
                    "practice_id": "152",
                    "data": {
                      "provider_id": "2231",
                      "role": "staff",
                      "status": "active"
                    }
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Return any 2xx status to acknowledge the event. Anything else, or no response within 5 seconds, is retried."
          }
        }
      }
    },
    "practice.connected": {
      "post": {
        "operationId": "practice_connected_event",
        "summary": "Practice connected",
        "description": "A practice connected to your organization. You also learn this from the\nconnect flow. This event also covers connections Thea sets up for you,\nsuch as your sandbox test practice; Thea sends you those practice keys\ndirectly.\n\nDelivered as a signed `POST` to every active endpoint subscribed to\n`practice.connected`. Respond with any 2xx status within 5 seconds.\n",
        "security": [],
        "parameters": [
          {
            "in": "header",
            "name": "Thea-Signature",
            "required": true,
            "description": "See [Verify signatures](https://docs.theahealth.com/guides/webhooks/#verify-signatures).",
            "schema": {
              "type": "string"
            }
          },
          {
            "in": "header",
            "name": "Thea-Event-Id",
            "required": true,
            "description": "The event's `id`, repeated as a header.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "allOf": [
                  {
                    "$ref": "#/components/schemas/Event"
                  },
                  {
                    "type": "object",
                    "properties": {
                      "type": {
                        "const": "practice.connected"
                      },
                      "data": {
                        "type": "object",
                        "required": [
                          "connected_at"
                        ],
                        "properties": {
                          "connected_at": {
                            "type": "string",
                            "format": "date-time"
                          }
                        }
                      }
                    }
                  }
                ]
              },
              "examples": {
                "event": {
                  "value": {
                    "id": "50310",
                    "type": "practice.connected",
                    "created_at": "2026-11-04T18:05:12Z",
                    "practice_id": "152",
                    "data": {
                      "connected_at": "2026-11-04T18:05:12Z"
                    }
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Return any 2xx status to acknowledge the event. Anything else, or no response within 5 seconds, is retried."
          }
        }
      }
    },
    "practice.disconnected": {
      "post": {
        "operationId": "practice_disconnected_event",
        "summary": "Practice disconnected",
        "description": "A practice is no longer connected to your organization. Its practice key\nstopped working immediately. Open cases carry on in Thea. Stop offering\neConsults for the practice and clear its roster mapping.\n\nDelivered as a signed `POST` to every active endpoint subscribed to\n`practice.disconnected`. Respond with any 2xx status within 5 seconds.\n",
        "security": [],
        "parameters": [
          {
            "in": "header",
            "name": "Thea-Signature",
            "required": true,
            "description": "See [Verify signatures](https://docs.theahealth.com/guides/webhooks/#verify-signatures).",
            "schema": {
              "type": "string"
            }
          },
          {
            "in": "header",
            "name": "Thea-Event-Id",
            "required": true,
            "description": "The event's `id`, repeated as a header.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "allOf": [
                  {
                    "$ref": "#/components/schemas/Event"
                  },
                  {
                    "type": "object",
                    "properties": {
                      "type": {
                        "const": "practice.disconnected"
                      },
                      "data": {
                        "type": "object",
                        "required": [
                          "disconnected_at",
                          "disconnected_by"
                        ],
                        "properties": {
                          "disconnected_at": {
                            "type": "string",
                            "format": "date-time"
                          },
                          "disconnected_by": {
                            "type": "string",
                            "enum": [
                              "practice",
                              "partner",
                              "thea"
                            ],
                            "description": "Who ended the connection."
                          }
                        }
                      }
                    }
                  }
                ]
              },
              "examples": {
                "event": {
                  "value": {
                    "id": "50311",
                    "type": "practice.disconnected",
                    "created_at": "2026-11-04T18:05:12Z",
                    "practice_id": "152",
                    "data": {
                      "disconnected_at": "2026-11-04T18:05:12Z",
                      "disconnected_by": "practice"
                    }
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Return any 2xx status to acknowledge the event. Anything else, or no response within 5 seconds, is retried."
          }
        }
      }
    }
  },
  "components": {
    "securitySchemes": {
      "ApiKey": {
        "type": "http",
        "scheme": "bearer",
        "bearerFormat": "thea_sk_{mode}_{secret}",
        "description": "Send your secret API key as a bearer token:\n\n```\nAuthorization: Bearer thea_sk_test_…\n```\n\nYour **partner key** identifies your organization. Partner keys start\nwith `thea_sk_test_` (sandbox) or `thea_sk_live_` (live). Send it on\nevery request. Thea never accepts keys in query strings.\n"
      },
      "PracticeKey": {
        "type": "apiKey",
        "in": "header",
        "name": "Thea-Practice-Key",
        "description": "A **practice key** identifies one connected practice. You get one when\nthe practice [connects](https://docs.theahealth.com/concepts/practices/#connect-a-practice)\nto your organization. Practice keys start with `thea_pk_test_` or\n`thea_pk_live_` and don't expire.\n\n```\nThea-Practice-Key: thea_pk_test_…\n```\n\nSend it, together with your partner key, on every request about a\npractice's data. It only works with the partner key it was issued to,\nand the request acts on that practice with the scopes its admin\napproved. Requests that don't need a practice key ignore one if you\nsend it.\n"
      }
    },
    "schemas": {
      "Specialty": {
        "type": "object",
        "description": "A specialty that eConsults can be requested from.",
        "required": [
          "id",
          "name"
        ],
        "properties": {
          "id": {
            "type": "string",
            "description": "Unique identifier for the specialty.",
            "examples": [
              "12"
            ]
          },
          "name": {
            "type": "string",
            "description": "Display name.",
            "examples": [
              "Cardiology"
            ]
          }
        }
      },
      "SpecialtyList": {
        "type": "object",
        "required": [
          "data",
          "has_more",
          "next_cursor"
        ],
        "properties": {
          "data": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Specialty"
            }
          },
          "has_more": {
            "type": "boolean",
            "description": "Always `false`. The full list is returned in one page."
          },
          "next_cursor": {
            "type": "null"
          }
        }
      },
      "Scope": {
        "type": "string",
        "description": "A permission a practice approved for your organization.",
        "enum": [
          "patients:read",
          "patients:write",
          "cases:read",
          "cases:write",
          "messages:read",
          "messages:write",
          "files:write",
          "providers:read",
          "providers:write"
        ]
      },
      "Address": {
        "type": "object",
        "properties": {
          "line1": {
            "type": [
              "string",
              "null"
            ],
            "maxLength": 255,
            "examples": [
              "410 Elm Street"
            ]
          },
          "city": {
            "type": [
              "string",
              "null"
            ],
            "maxLength": 100,
            "examples": [
              "Austin"
            ]
          },
          "state": {
            "type": [
              "string",
              "null"
            ],
            "description": "Two-letter US state code.",
            "maxLength": 100,
            "examples": [
              "TX"
            ]
          },
          "postal_code": {
            "type": [
              "string",
              "null"
            ],
            "maxLength": 20,
            "examples": [
              "78701"
            ]
          }
        }
      },
      "Patient": {
        "type": "object",
        "description": "A patient of a practice. Social Security numbers and insurance details are\nnever returned by the API.\n",
        "required": [
          "id",
          "practice_id",
          "external_id",
          "first_name",
          "last_name",
          "date_of_birth",
          "gender",
          "email",
          "phone",
          "address",
          "created_at",
          "updated_at"
        ],
        "properties": {
          "id": {
            "type": "string",
            "examples": [
              "18342"
            ]
          },
          "practice_id": {
            "type": "string",
            "examples": [
              "152"
            ]
          },
          "external_id": {
            "type": [
              "string",
              "null"
            ],
            "description": "Your identifier for the patient, if you set one.",
            "examples": [
              "acme-pt-99812"
            ]
          },
          "first_name": {
            "type": "string",
            "examples": [
              "Avery"
            ]
          },
          "last_name": {
            "type": "string",
            "examples": [
              "Johnson"
            ]
          },
          "date_of_birth": {
            "type": [
              "string",
              "null"
            ],
            "format": "date",
            "examples": [
              "1984-03-12"
            ]
          },
          "gender": {
            "type": [
              "string",
              "null"
            ],
            "enum": [
              "female",
              "male",
              "other",
              "unknown",
              null
            ],
            "examples": [
              "female"
            ]
          },
          "email": {
            "type": [
              "string",
              "null"
            ],
            "format": "email",
            "examples": [
              "avery.johnson@example.com"
            ]
          },
          "phone": {
            "type": [
              "string",
              "null"
            ],
            "examples": [
              "+15125550142"
            ]
          },
          "address": {
            "oneOf": [
              {
                "$ref": "#/components/schemas/Address"
              },
              {
                "type": "null"
              }
            ]
          },
          "created_at": {
            "type": "string",
            "format": "date-time",
            "examples": [
              "2026-11-04T15:24:11Z"
            ]
          },
          "updated_at": {
            "type": "string",
            "format": "date-time",
            "examples": [
              "2026-11-04T15:24:11Z"
            ]
          }
        }
      },
      "PatientInput": {
        "type": "object",
        "description": "Patient demographics.",
        "required": [
          "first_name",
          "last_name",
          "date_of_birth"
        ],
        "properties": {
          "external_id": {
            "type": [
              "string",
              "null"
            ],
            "maxLength": 255,
            "description": "Your identifier for this patient (for example, your EHR's patient ID).\nThea matches on `external_id` first, within the practice, so sending it\nmakes matching exact.\n",
            "examples": [
              "acme-pt-99812"
            ]
          },
          "first_name": {
            "type": "string",
            "minLength": 1,
            "maxLength": 100,
            "examples": [
              "Avery"
            ]
          },
          "last_name": {
            "type": "string",
            "minLength": 1,
            "maxLength": 100,
            "examples": [
              "Johnson"
            ]
          },
          "date_of_birth": {
            "type": "string",
            "format": "date",
            "examples": [
              "1984-03-12"
            ]
          },
          "gender": {
            "type": [
              "string",
              "null"
            ],
            "enum": [
              "female",
              "male",
              "other",
              "unknown",
              null
            ],
            "examples": [
              "female"
            ]
          },
          "email": {
            "type": [
              "string",
              "null"
            ],
            "format": "email",
            "examples": [
              "avery.johnson@example.com"
            ]
          },
          "phone": {
            "type": [
              "string",
              "null"
            ],
            "description": "Mobile phone in E.164 format.",
            "maxLength": 100,
            "examples": [
              "+15125550142"
            ]
          },
          "address": {
            "oneOf": [
              {
                "$ref": "#/components/schemas/Address"
              },
              {
                "type": "null"
              }
            ]
          }
        }
      },
      "PatientUpsert": {
        "description": "Create a patient, or update the matching one. Thea looks only in your\npractice key's practice: first by `external_id`, then by first name, last\nname, and date of birth.\n",
        "allOf": [
          {
            "type": "object",
            "properties": {
              "practice_id": {
                "type": "string",
                "description": "Optional. If you send it, it must be the practice your practice key belongs to.",
                "examples": [
                  "152"
                ]
              }
            }
          },
          {
            "$ref": "#/components/schemas/PatientInput"
          }
        ]
      },
      "PatientList": {
        "type": "object",
        "description": "A page of results. See [Pagination](https://docs.theahealth.com/concepts/pagination/).",
        "required": [
          "data",
          "has_more",
          "next_cursor"
        ],
        "properties": {
          "data": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Patient"
            }
          },
          "has_more": {
            "type": "boolean",
            "description": "Whether there are more results after this page."
          },
          "next_cursor": {
            "type": [
              "string",
              "null"
            ],
            "description": "Pass as `cursor` to fetch the next page. `null` on the last page.",
            "examples": [
              "eyJ1IjoiMjAyNi0xMS0wNFQxNToyNDoxMVoiLCJpIjoiMTgzNDIifQ"
            ]
          }
        }
      },
      "Case": {
        "type": "object",
        "description": "An eConsult from a practice provider to one or more specialties.",
        "required": [
          "id",
          "practice_id",
          "patient_id",
          "status",
          "requesting_provider",
          "created_by",
          "specialties",
          "question",
          "patient_consent_given",
          "responding_specialists",
          "files",
          "external_ids",
          "url",
          "created_at",
          "updated_at",
          "closed_at",
          "closed_by",
          "first_specialist_response_at"
        ],
        "properties": {
          "id": {
            "type": "string",
            "examples": [
              "a1f3c9e27b4d4e0f9c6b2d8e5f7a1c3b"
            ]
          },
          "practice_id": {
            "type": "string",
            "examples": [
              "152"
            ]
          },
          "patient_id": {
            "type": "string",
            "examples": [
              "18342"
            ]
          },
          "status": {
            "$ref": "#/components/schemas/CaseStatus"
          },
          "requesting_provider": {
            "$ref": "#/components/schemas/Provider"
          },
          "created_by": {
            "description": "The staff member who created the case on the provider's behalf, or `null` if the provider did.",
            "oneOf": [
              {
                "$ref": "#/components/schemas/Provider"
              },
              {
                "type": "null"
              }
            ]
          },
          "specialties": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Specialty"
            }
          },
          "question": {
            "type": "string",
            "description": "The clinical question for the specialist.",
            "examples": [
              "55F with new-onset palpitations; Holter shows frequent PVCs (8% burden). Echo normal. Is a beta-blocker reasonable first-line, and when should she see cardiology in person?"
            ]
          },
          "patient_consent_given": {
            "type": "boolean",
            "description": "Whether the patient consented to the eConsult. Always `true` for cases created through the API."
          },
          "responding_specialists": {
            "type": "array",
            "description": "Specialists who have replied on the case, in the order they first\nreplied. Empty until a specialist replies. Thea doesn't show who a case\nis assigned to before they respond.\n",
            "items": {
              "$ref": "#/components/schemas/Specialist"
            }
          },
          "files": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/File"
            }
          },
          "external_ids": {
            "$ref": "#/components/schemas/ExternalIds"
          },
          "url": {
            "type": "string",
            "format": "uri",
            "description": "Opens the case in the Thea web app. Clinicians sign in to Thea as usual.",
            "examples": [
              "https://app.theahealth.com/?caseId=a1f3c9e27b4d4e0f9c6b2d8e5f7a1c3b"
            ]
          },
          "created_at": {
            "type": "string",
            "format": "date-time",
            "examples": [
              "2026-11-04T15:24:11Z"
            ]
          },
          "updated_at": {
            "type": "string",
            "format": "date-time",
            "examples": [
              "2026-11-04T16:02:45Z"
            ]
          },
          "closed_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time",
            "examples": [
              null
            ]
          },
          "closed_by": {
            "description": "The provider who closed the case, or `null` while it's open.",
            "oneOf": [
              {
                "$ref": "#/components/schemas/Provider"
              },
              {
                "type": "null"
              }
            ]
          },
          "first_specialist_response_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time",
            "description": "When a specialist first replied, or `null` if no one has yet.",
            "examples": [
              "2026-11-04T16:02:45Z"
            ]
          }
        }
      },
      "CaseCreate": {
        "type": "object",
        "description": "Send **either** `patient_id` (an existing patient) **or** `patient` (demographics\nto match or create a patient in the same request), not both.\n",
        "required": [
          "provider",
          "specialty_ids",
          "question",
          "patient_consent_given"
        ],
        "properties": {
          "practice_id": {
            "type": "string",
            "description": "Optional. If you send it, it must be the practice your practice key belongs to.",
            "examples": [
              "152"
            ]
          },
          "patient_id": {
            "type": "string",
            "examples": [
              "18342"
            ]
          },
          "patient": {
            "allOf": [
              {
                "$ref": "#/components/schemas/PatientInput"
              }
            ],
            "description": "The patient's demographics, instead of `patient_id`. Thea uses the\npractice's matching patient (same `external_id`, then same name and date\nof birth, in this practice only), or creates one, so a patient only exists\nin Thea once you send a consult for them. See\n[Identify the patient](https://docs.theahealth.com/guides/create-an-econsult/#identify-the-patient).\n"
          },
          "provider": {
            "allOf": [
              {
                "$ref": "#/components/schemas/ProviderSelector"
              }
            ],
            "description": "The clinician the consult is filed for: an `active` roster member with\nrole `provider` in this practice. Specialists reply to them, and they're\nnotified the same way as for consults created in Thea. Otherwise the\nrequest fails with `provider-not-active`.\n"
          },
          "created_by_email": {
            "type": "string",
            "format": "email",
            "description": "Optional. An `active` staff member in the same practice who is creating\nthe case on the provider's behalf. Shown in Thea as the case's creator.\n",
            "examples": [
              "ma@riverbend.example"
            ]
          },
          "specialty_ids": {
            "type": "array",
            "minItems": 1,
            "uniqueItems": true,
            "description": "IDs from `GET /specialties`.",
            "items": {
              "type": "string"
            },
            "examples": [
              [
                "12"
              ]
            ]
          },
          "question": {
            "type": "string",
            "minLength": 1,
            "maxLength": 20000,
            "description": "The clinical question, with the relevant history, findings, and what you want answered.",
            "examples": [
              "55F with new-onset palpitations; Holter shows frequent PVCs (8% burden). Echo normal. Is a beta-blocker reasonable first-line, and when should she see cardiology in person?"
            ]
          },
          "patient_consent_given": {
            "type": "boolean",
            "description": "Must be `true`. Confirms the patient consented to an eConsult. Otherwise the request fails with `consent-required`."
          },
          "external_ids": {
            "$ref": "#/components/schemas/ExternalIds"
          }
        }
      },
      "CaseList": {
        "type": "object",
        "description": "A page of results. See [Pagination](https://docs.theahealth.com/concepts/pagination/).",
        "required": [
          "data",
          "has_more",
          "next_cursor"
        ],
        "properties": {
          "data": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Case"
            }
          },
          "has_more": {
            "type": "boolean",
            "description": "Whether there are more results after this page."
          },
          "next_cursor": {
            "type": [
              "string",
              "null"
            ],
            "description": "Pass as `cursor` to fetch the next page. `null` on the last page.",
            "examples": [
              "eyJ1IjoiMjAyNi0xMS0wNFQxNToyNDoxMVoiLCJpIjoiMTgzNDIifQ"
            ]
          }
        }
      },
      "CaseStatus": {
        "type": "string",
        "description": "`open` while the consult is active. `closed` once a provider has closed it.\nSpecialists can't close cases. Closed cases can be reopened.\n",
        "enum": [
          "open",
          "closed"
        ]
      },
      "Specialist": {
        "type": "object",
        "description": "A specialist who has replied on the case.",
        "required": [
          "id",
          "name",
          "specialty"
        ],
        "properties": {
          "id": {
            "type": "string",
            "examples": [
              "3051"
            ]
          },
          "name": {
            "type": "string",
            "examples": [
              "Dr. Priya Shah"
            ]
          },
          "specialty": {
            "$ref": "#/components/schemas/Specialty"
          }
        }
      },
      "Provider": {
        "type": "object",
        "description": "A clinician or staff member on the practice's roster.",
        "required": [
          "id",
          "name",
          "email"
        ],
        "properties": {
          "id": {
            "type": "string",
            "examples": [
              "2210"
            ]
          },
          "name": {
            "type": "string",
            "examples": [
              "Dr. Sam Rivera"
            ]
          },
          "email": {
            "type": "string",
            "format": "email",
            "examples": [
              "sam.rivera@riverbend.example"
            ]
          }
        }
      },
      "ProviderSelector": {
        "description": "Identifies one clinician on the practice's roster by exactly one of their\nThea roster `id`, their `npi`, or their Thea `email`.\n",
        "oneOf": [
          {
            "type": "object",
            "title": "By roster ID",
            "required": [
              "id"
            ],
            "additionalProperties": false,
            "properties": {
              "id": {
                "type": "string",
                "description": "The `id` from `GET /practices/{practice_id}/providers`.",
                "examples": [
                  "2210"
                ]
              }
            }
          },
          {
            "type": "object",
            "title": "By NPI",
            "required": [
              "npi"
            ],
            "additionalProperties": false,
            "properties": {
              "npi": {
                "type": "string",
                "pattern": "^\\d{10}$",
                "examples": [
                  "1234567890"
                ]
              }
            }
          },
          {
            "type": "object",
            "title": "By email",
            "required": [
              "email"
            ],
            "additionalProperties": false,
            "properties": {
              "email": {
                "type": "string",
                "format": "email",
                "examples": [
                  "sam.rivera@riverbend.example"
                ]
              }
            }
          }
        ]
      },
      "ExternalIds": {
        "type": "object",
        "description": "Your own identifiers for this record, such as your referral ID. Thea stores\nthem and echoes them on every response and webhook for the case, so you can\nmap it back without keeping Thea's IDs. Keys are lowercase snake_case.\n",
        "maxProperties": 10,
        "propertyNames": {
          "pattern": "^[a-z][a-z0-9_]{0,39}$"
        },
        "additionalProperties": {
          "type": "string",
          "maxLength": 255
        },
        "examples": [
          {
            "referral_id": "sigma-ref-99812"
          }
        ]
      },
      "RosterRole": {
        "type": "string",
        "description": "`provider` is a billable clinician seat. `staff` is clinical staff who can\ncreate cases and post messages on a provider's behalf; staff aren't billed.\n`admin` is a practice admin or biller who can see cases but can't create\nthem or post messages.\n",
        "enum": [
          "provider",
          "staff",
          "admin"
        ]
      },
      "RosterStatus": {
        "type": "string",
        "description": "`active` can use Thea. `pending` is waiting for the practice admin to\napprove a seat. `inactive` has been removed or deactivated.\n",
        "enum": [
          "active",
          "pending",
          "inactive"
        ]
      },
      "RosterProvider": {
        "type": "object",
        "description": "A clinician or staff member on a practice's Thea roster.",
        "required": [
          "id",
          "practice_id",
          "first_name",
          "last_name",
          "email",
          "npi",
          "role",
          "status",
          "updated_at"
        ],
        "properties": {
          "id": {
            "type": "string",
            "examples": [
              "2210"
            ]
          },
          "practice_id": {
            "type": "string",
            "examples": [
              "152"
            ]
          },
          "first_name": {
            "type": "string",
            "examples": [
              "Sam"
            ]
          },
          "last_name": {
            "type": "string",
            "examples": [
              "Rivera"
            ]
          },
          "email": {
            "type": "string",
            "format": "email",
            "description": "Unique across Thea. One account per email, even across practices.",
            "examples": [
              "sam.rivera@riverbend.example"
            ]
          },
          "npi": {
            "type": [
              "string",
              "null"
            ],
            "description": "The clinician's NPI, or `null` if Thea doesn't have it.",
            "examples": [
              "1234567890"
            ]
          },
          "role": {
            "$ref": "#/components/schemas/RosterRole"
          },
          "status": {
            "$ref": "#/components/schemas/RosterStatus"
          },
          "updated_at": {
            "type": "string",
            "format": "date-time",
            "examples": [
              "2026-11-04T15:24:11Z"
            ]
          }
        }
      },
      "RosterProviderList": {
        "type": "object",
        "description": "A page of results. See [Pagination](https://docs.theahealth.com/concepts/pagination/).",
        "required": [
          "data",
          "has_more",
          "next_cursor"
        ],
        "properties": {
          "data": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/RosterProvider"
            }
          },
          "has_more": {
            "type": "boolean",
            "description": "Whether there are more results after this page."
          },
          "next_cursor": {
            "type": [
              "string",
              "null"
            ],
            "description": "Pass as `cursor` to fetch the next page. `null` on the last page.",
            "examples": [
              null
            ]
          }
        }
      },
      "ProviderRequestCreate": {
        "type": "object",
        "description": "A clinician who needs a Thea seat. The practice admin decides.",
        "required": [
          "first_name",
          "last_name",
          "email"
        ],
        "properties": {
          "first_name": {
            "type": "string",
            "minLength": 1,
            "examples": [
              "Jordan"
            ]
          },
          "last_name": {
            "type": "string",
            "minLength": 1,
            "examples": [
              "Okafor"
            ]
          },
          "email": {
            "type": "string",
            "format": "email",
            "description": "Becomes their Thea login if the admin approves.",
            "examples": [
              "jordan.okafor@riverbend.example"
            ]
          },
          "npi": {
            "type": "string",
            "pattern": "^\\d{10}$",
            "examples": [
              "1987654321"
            ]
          },
          "role": {
            "type": "string",
            "enum": [
              "provider",
              "staff"
            ],
            "default": "provider",
            "description": "The kind of seat being requested."
          }
        }
      },
      "ProviderRequest": {
        "type": "object",
        "description": "A request for the practice admin to add someone to the roster.",
        "required": [
          "id",
          "practice_id",
          "email",
          "role",
          "status",
          "created_at"
        ],
        "properties": {
          "id": {
            "type": "string",
            "examples": [
              "preq_5c21"
            ]
          },
          "practice_id": {
            "type": "string",
            "examples": [
              "152"
            ]
          },
          "email": {
            "type": "string",
            "format": "email",
            "examples": [
              "jordan.okafor@riverbend.example"
            ]
          },
          "role": {
            "type": "string",
            "enum": [
              "provider",
              "staff"
            ]
          },
          "status": {
            "type": "string",
            "enum": [
              "pending_admin_approval",
              "approved",
              "declined"
            ],
            "description": "`approved` also sends a `provider.activated` webhook. Post the same email\nagain to check on a request; you get the existing one back.\n"
          },
          "created_at": {
            "type": "string",
            "format": "date-time",
            "examples": [
              "2026-11-04T15:24:11Z"
            ]
          }
        }
      },
      "ConnectCodeExchange": {
        "type": "object",
        "required": [
          "grant_type",
          "code",
          "redirect_uri"
        ],
        "properties": {
          "grant_type": {
            "type": "string",
            "const": "authorization_code"
          },
          "code": {
            "type": "string",
            "description": "The `code` Thea added to your `redirect_uri`. Single-use, and expires after 10 minutes.",
            "examples": [
              "cc_9Hd2kQ7xLm"
            ]
          },
          "redirect_uri": {
            "type": "string",
            "format": "uri",
            "description": "The same registered `redirect_uri` you sent the admin to Thea with.",
            "examples": [
              "https://app.acme-ehr.example/integrations/thea/callback"
            ]
          }
        }
      },
      "PracticeConnection": {
        "type": "object",
        "description": "A practice that has connected to your organization.",
        "required": [
          "practice_id",
          "practice_name",
          "practice_key",
          "scopes",
          "connected_at"
        ],
        "properties": {
          "practice_id": {
            "type": "string",
            "examples": [
              "152"
            ]
          },
          "practice_name": {
            "type": "string",
            "examples": [
              "Riverbend Family Medicine"
            ]
          },
          "practice_key": {
            "type": "string",
            "description": "Send it as `Thea-Practice-Key` with your partner key on every request for\nthis practice. **This is the only time it's returned**, so store it\nsecurely now. It doesn't expire. It stops working if the practice\ndisconnects, or when you connect the practice again (which issues a new\nkey).\n",
            "examples": [
              "thea_pk_test_7Rk2pQ9vXa3mLc8tYb4nWe6s"
            ]
          },
          "scopes": {
            "type": "array",
            "description": "What the practice admin approved for your organization in this practice.",
            "items": {
              "$ref": "#/components/schemas/Scope"
            }
          },
          "connected_at": {
            "type": "string",
            "format": "date-time",
            "examples": [
              "2026-11-04T15:04:05Z"
            ]
          }
        }
      },
      "File": {
        "type": "object",
        "description": "Metadata for a file attached to a case.",
        "required": [
          "id",
          "filename",
          "content_type",
          "size_bytes"
        ],
        "properties": {
          "id": {
            "type": "string",
            "examples": [
              "6d0e4f2a9b"
            ]
          },
          "filename": {
            "type": "string",
            "examples": [
              "ecg-2026-11-02.pdf"
            ]
          },
          "content_type": {
            "type": "string",
            "examples": [
              "application/pdf"
            ]
          },
          "size_bytes": {
            "type": [
              "integer",
              "null"
            ],
            "description": "File size, or `null` for some files added before the API existed.",
            "examples": [
              482113
            ]
          }
        }
      },
      "FileWithDownload": {
        "allOf": [
          {
            "$ref": "#/components/schemas/File"
          },
          {
            "type": "object",
            "required": [
              "download_url",
              "download_url_expires_at"
            ],
            "properties": {
              "download_url": {
                "type": "string",
                "format": "uri",
                "description": "A short-lived URL to download the file. Don't store it; request a new\none each time you need the file.\n",
                "examples": [
                  "https://s3.us-east-2.amazonaws.com/example-bucket/6d0e4f2a9b?X-Amz-Expires=300&X-Amz-Signature=8b1f0c4e"
                ]
              },
              "download_url_expires_at": {
                "type": "string",
                "format": "date-time",
                "description": "When `download_url` stops working (5 minutes after issue).",
                "examples": [
                  "2026-11-04T15:29:11Z"
                ]
              }
            }
          }
        ]
      },
      "Message": {
        "type": "object",
        "required": [
          "id",
          "case_id",
          "body",
          "sender",
          "files",
          "created_at"
        ],
        "properties": {
          "id": {
            "type": "string",
            "examples": [
              "88123"
            ]
          },
          "case_id": {
            "type": "string",
            "examples": [
              "a1f3c9e27b4d4e0f9c6b2d8e5f7a1c3b"
            ]
          },
          "body": {
            "type": "string",
            "description": "Plain text.",
            "examples": [
              "Thanks. Her TSH came back normal at 1.9 if that changes anything."
            ]
          },
          "sender": {
            "$ref": "#/components/schemas/MessageSender"
          },
          "files": {
            "type": "array",
            "description": "Files posted with this message, from Thea or `POST /cases/{case_id}/files`. Download them with `GET /files/{file_id}`.",
            "items": {
              "$ref": "#/components/schemas/File"
            }
          },
          "created_at": {
            "type": "string",
            "format": "date-time",
            "examples": [
              "2026-11-04T16:10:02Z"
            ]
          }
        }
      },
      "MessageCreate": {
        "type": "object",
        "required": [
          "body"
        ],
        "properties": {
          "body": {
            "type": "string",
            "minLength": 1,
            "maxLength": 20000,
            "description": "Plain text. Shown to specialists as \"via\" your partner name.",
            "examples": [
              "Thanks. Her TSH came back normal at 1.9 if that changes anything."
            ]
          },
          "author_provider_id": {
            "type": "string",
            "description": "Optional. The roster `id` of the `active` provider or staff member\nposting the message, in the case's practice. Defaults to the case's\nrequesting provider. Admins can't post.\n",
            "examples": [
              "2210"
            ]
          }
        }
      },
      "MessageList": {
        "type": "object",
        "description": "A page of results. See [Pagination](https://docs.theahealth.com/concepts/pagination/).",
        "required": [
          "data",
          "has_more",
          "next_cursor"
        ],
        "properties": {
          "data": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Message"
            }
          },
          "has_more": {
            "type": "boolean",
            "description": "Whether there are more results after this page."
          },
          "next_cursor": {
            "type": [
              "string",
              "null"
            ],
            "description": "Pass as `cursor` to fetch the next page. `null` on the last page.",
            "examples": [
              "eyJ1IjoiMjAyNi0xMS0wNFQxNToyNDoxMVoiLCJpIjoiMTgzNDIifQ"
            ]
          }
        }
      },
      "MessageSender": {
        "type": "object",
        "required": [
          "type",
          "id",
          "name",
          "via"
        ],
        "properties": {
          "type": {
            "type": "string",
            "enum": [
              "provider",
              "staff",
              "specialist",
              "support"
            ],
            "description": "`provider` is a practice clinician, `staff` practice staff, `specialist` a specialist answering the case, `support` the Thea care team."
          },
          "id": {
            "type": "string",
            "description": "For `provider` and `staff`, their roster `id`.",
            "examples": [
              "2210"
            ]
          },
          "name": {
            "type": "string",
            "examples": [
              "Dr. Sam Rivera"
            ]
          },
          "via": {
            "type": [
              "string",
              "null"
            ],
            "description": "Your partner name if the message was posted through the API, otherwise `null`.",
            "examples": [
              "Acme EHR"
            ]
          }
        }
      },
      "WebhookEndpoint": {
        "type": "object",
        "required": [
          "id",
          "url",
          "description",
          "events",
          "active",
          "created_at"
        ],
        "properties": {
          "id": {
            "type": "string",
            "examples": [
              "17"
            ]
          },
          "url": {
            "type": "string",
            "format": "uri",
            "examples": [
              "https://hooks.acme-ehr.example/thea"
            ]
          },
          "description": {
            "type": [
              "string",
              "null"
            ],
            "examples": [
              "Production consult sync"
            ]
          },
          "events": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/EventType"
            },
            "examples": [
              [
                "case.message.created",
                "case.closed"
              ]
            ]
          },
          "active": {
            "type": "boolean",
            "description": "`false` once deliveries have failed for 24 hours. Delete the endpoint and create it again to resume."
          },
          "created_at": {
            "type": "string",
            "format": "date-time",
            "examples": [
              "2026-11-04T15:24:11Z"
            ]
          }
        }
      },
      "WebhookEndpointWithSecret": {
        "allOf": [
          {
            "$ref": "#/components/schemas/WebhookEndpoint"
          },
          {
            "type": "object",
            "required": [
              "secret"
            ],
            "properties": {
              "secret": {
                "type": "string",
                "description": "The signing secret for this endpoint. **Only returned when the endpoint\nis created.** Store it securely and use it to\n[verify signatures](https://docs.theahealth.com/guides/webhooks/#verify-signatures).\n",
                "examples": [
                  "thea_whsec_9fK2mQ7vR1xL0pT4sW8yB3nD6hJ5gC"
                ]
              }
            }
          }
        ]
      },
      "WebhookEndpointCreate": {
        "type": "object",
        "required": [
          "url",
          "events"
        ],
        "properties": {
          "url": {
            "type": "string",
            "format": "uri",
            "pattern": "^https://",
            "maxLength": 2048,
            "description": "An HTTPS URL that accepts `POST` requests.",
            "examples": [
              "https://hooks.acme-ehr.example/thea"
            ]
          },
          "events": {
            "type": "array",
            "minItems": 1,
            "uniqueItems": true,
            "items": {
              "$ref": "#/components/schemas/EventType"
            },
            "examples": [
              [
                "case.message.created",
                "case.closed"
              ]
            ]
          },
          "description": {
            "type": [
              "string",
              "null"
            ],
            "maxLength": 255,
            "examples": [
              "Production consult sync"
            ]
          }
        }
      },
      "WebhookEndpointList": {
        "type": "object",
        "description": "A page of results. See [Pagination](https://docs.theahealth.com/concepts/pagination/).",
        "required": [
          "data",
          "has_more",
          "next_cursor"
        ],
        "properties": {
          "data": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/WebhookEndpoint"
            }
          },
          "has_more": {
            "type": "boolean",
            "description": "Whether there are more results after this page."
          },
          "next_cursor": {
            "type": [
              "string",
              "null"
            ],
            "description": "Pass as `cursor` to fetch the next page. `null` on the last page.",
            "examples": [
              "eyJ1IjoiMjAyNi0xMS0wNFQxNToyNDoxMVoiLCJpIjoiMTgzNDIifQ"
            ]
          }
        }
      },
      "WebhookTestResult": {
        "type": "object",
        "required": [
          "event_id",
          "delivered",
          "status_code",
          "error"
        ],
        "properties": {
          "event_id": {
            "type": "string",
            "examples": [
              "50212"
            ]
          },
          "delivered": {
            "type": "boolean",
            "description": "Whether your endpoint returned a 2xx status within 5 seconds."
          },
          "status_code": {
            "type": [
              "integer",
              "null"
            ],
            "description": "The HTTP status your endpoint returned, or `null` if it didn't respond.",
            "examples": [
              200
            ]
          },
          "error": {
            "type": [
              "string",
              "null"
            ],
            "description": "Why delivery failed, for example `timeout` or `connection_refused`.",
            "examples": [
              null
            ]
          }
        }
      },
      "EventType": {
        "type": "string",
        "description": "An event you can subscribe a webhook endpoint to.",
        "enum": [
          "case.created",
          "case.closed",
          "case.reopened",
          "case.message.created",
          "patient.created",
          "patient.updated",
          "provider.activated",
          "provider.deactivated",
          "provider.updated",
          "practice.connected",
          "practice.disconnected"
        ]
      },
      "Event": {
        "type": "object",
        "description": "The body of every webhook delivery.",
        "required": [
          "id",
          "type",
          "created_at",
          "practice_id",
          "data"
        ],
        "properties": {
          "id": {
            "type": "string",
            "description": "Unique per event. Retries of the same event reuse it, so use it to skip duplicates.",
            "examples": [
              "50211"
            ]
          },
          "type": {
            "type": "string",
            "examples": [
              "case.closed"
            ]
          },
          "created_at": {
            "type": "string",
            "format": "date-time",
            "examples": [
              "2026-11-04T17:30:00Z"
            ]
          },
          "practice_id": {
            "type": "string",
            "examples": [
              "152"
            ]
          },
          "data": {
            "type": "object",
            "description": "IDs and a few non-sensitive fields. Fetch the resource for full details."
          }
        }
      },
      "Problem": {
        "type": "object",
        "description": "An [RFC 9457](https://www.rfc-editor.org/rfc/rfc9457) problem details object.",
        "required": [
          "type",
          "title",
          "status",
          "request_id"
        ],
        "properties": {
          "type": {
            "type": "string",
            "format": "uri",
            "description": "Identifies the kind of error. Links to its entry in the [error catalog](https://docs.theahealth.com/concepts/errors/).",
            "examples": [
              "https://docs.theahealth.com/concepts/errors/#not-found"
            ]
          },
          "title": {
            "type": "string",
            "description": "A short summary of the error type. Doesn't change between occurrences.",
            "examples": [
              "Resource not found"
            ]
          },
          "status": {
            "type": "integer",
            "description": "The HTTP status code.",
            "examples": [
              404
            ]
          },
          "detail": {
            "type": "string",
            "description": "What went wrong in this specific request.",
            "examples": [
              "No case a1f3c9e27b4d4e0f9c6b2d8e5f7a1c3b in practice 152."
            ]
          },
          "instance": {
            "type": "string",
            "description": "The request path.",
            "examples": [
              "/api/partner/v1/cases/a1f3c9e27b4d4e0f9c6b2d8e5f7a1c3b"
            ]
          },
          "request_id": {
            "type": "string",
            "description": "Include this when you contact support.",
            "examples": [
              "req_8c41f0e2d7b9"
            ]
          }
        }
      },
      "ValidationProblem": {
        "allOf": [
          {
            "$ref": "#/components/schemas/Problem"
          },
          {
            "type": "object",
            "required": [
              "errors"
            ],
            "properties": {
              "errors": {
                "type": "array",
                "description": "One entry per invalid field.",
                "items": {
                  "type": "object",
                  "required": [
                    "field",
                    "message"
                  ],
                  "properties": {
                    "field": {
                      "type": "string",
                      "description": "Dotted path to the field in the request body or query.",
                      "examples": [
                        "patient.date_of_birth"
                      ]
                    },
                    "message": {
                      "type": "string",
                      "examples": [
                        "Must be a date in YYYY-MM-DD format."
                      ]
                    }
                  }
                }
              }
            }
          }
        ]
      }
    },
    "parameters": {
      "PracticeId": {
        "name": "practice_id",
        "in": "path",
        "required": true,
        "description": "A practice connected to your organization.",
        "schema": {
          "type": "string"
        },
        "examples": {
          "practice": {
            "value": "152"
          }
        }
      },
      "PatientId": {
        "name": "patient_id",
        "in": "path",
        "required": true,
        "schema": {
          "type": "string"
        },
        "examples": {
          "patient": {
            "value": "18342"
          }
        }
      },
      "CaseId": {
        "name": "case_id",
        "in": "path",
        "required": true,
        "schema": {
          "type": "string"
        },
        "examples": {
          "case": {
            "value": "a1f3c9e27b4d4e0f9c6b2d8e5f7a1c3b"
          }
        }
      },
      "FileId": {
        "name": "file_id",
        "in": "path",
        "required": true,
        "schema": {
          "type": "string"
        },
        "examples": {
          "file": {
            "value": "6d0e4f2a9b"
          }
        }
      },
      "WebhookEndpointId": {
        "name": "webhook_endpoint_id",
        "in": "path",
        "required": true,
        "schema": {
          "type": "string"
        },
        "examples": {
          "endpoint": {
            "value": "17"
          }
        }
      },
      "UpdatedSince": {
        "name": "updated_since",
        "in": "query",
        "required": false,
        "description": "Only return records updated at or after this time. Use it to sync changes incrementally.",
        "schema": {
          "type": "string",
          "format": "date-time"
        },
        "examples": {
          "since": {
            "value": "2026-11-01T00:00:00Z"
          }
        }
      },
      "Cursor": {
        "name": "cursor",
        "in": "query",
        "required": false,
        "description": "The `next_cursor` from the previous page. Omit it for the first page.",
        "schema": {
          "type": "string"
        }
      },
      "Limit": {
        "name": "limit",
        "in": "query",
        "required": false,
        "description": "Maximum number of results to return.",
        "schema": {
          "type": "integer",
          "minimum": 1,
          "maximum": 100,
          "default": 25
        }
      },
      "IdempotencyKey": {
        "name": "Idempotency-Key",
        "in": "header",
        "required": false,
        "description": "A unique value (a UUID works well) that makes retrying this request safe.\nThea stores a successful (2xx) response for 24 hours and returns it again\nfor a retry with the same key. Error responses aren't stored. See [Idempotency](https://docs.theahealth.com/concepts/idempotency/).\n",
        "schema": {
          "type": "string",
          "maxLength": 255
        },
        "examples": {
          "uuid": {
            "value": "0f8e7a52-3c1d-4b9e-9a61-2d5c8f4b7e10"
          }
        }
      }
    },
    "headers": {
      "X-Request-Id": {
        "description": "A unique ID for this request. Include it when you contact support.",
        "schema": {
          "type": "string",
          "examples": [
            "req_8c41f0e2d7b9"
          ]
        }
      },
      "X-RateLimit-Limit": {
        "description": "Requests allowed per minute for this kind of request (reads or writes).\nRequests about a practice count against that practice's limit. Requests\nthat don't need a practice key (`GET /specialties`, webhook endpoints,\n`POST /connect/token`) share one partner-wide limit.\n",
        "schema": {
          "type": "integer",
          "examples": [
            300
          ]
        }
      },
      "X-RateLimit-Remaining": {
        "description": "Requests left in the current one-minute window.",
        "schema": {
          "type": "integer",
          "examples": [
            0
          ]
        }
      },
      "X-RateLimit-Reset": {
        "description": "When the current window resets, as a Unix timestamp in seconds.",
        "schema": {
          "type": "integer",
          "examples": [
            1793806011
          ]
        }
      },
      "Retry-After": {
        "description": "Seconds to wait before retrying.",
        "schema": {
          "type": "integer",
          "examples": [
            12
          ]
        }
      },
      "Idempotent-Replayed": {
        "description": "`true` when this response is a stored replay of an earlier request with the same `Idempotency-Key`.",
        "schema": {
          "type": "string",
          "enum": [
            "true"
          ]
        }
      },
      "Thea-Signature": {
        "description": "`t=<unix seconds>,v1=<hex HMAC-SHA256 of \"<t>.<raw body>\" using your endpoint secret>`.\nSee [Verify signatures](https://docs.theahealth.com/guides/webhooks/#verify-signatures).\n",
        "required": true,
        "schema": {
          "type": "string",
          "examples": [
            "t=1793806200,v1=5f3c1b0e9a7d4c2b8e6f0a1d3c5b7e9f2a4c6e8b0d1f3a5c7e9b2d4f6a8c0e1b"
          ]
        }
      },
      "Thea-Event-Id": {
        "description": "The event's `id`, repeated as a header.",
        "required": true,
        "schema": {
          "type": "string",
          "examples": [
            "50211"
          ]
        }
      }
    },
    "examples": {
      "Case": {
        "summary": "An open case with a specialist reply",
        "value": {
          "id": "a1f3c9e27b4d4e0f9c6b2d8e5f7a1c3b",
          "practice_id": "152",
          "patient_id": "18342",
          "status": "open",
          "requesting_provider": {
            "id": "2210",
            "name": "Dr. Sam Rivera",
            "email": "sam.rivera@riverbend.example"
          },
          "created_by": null,
          "specialties": [
            {
              "id": "12",
              "name": "Cardiology"
            }
          ],
          "question": "55F with new-onset palpitations; Holter shows frequent PVCs (8% burden). Echo normal. Is a beta-blocker reasonable first-line, and when should she see cardiology in person?",
          "patient_consent_given": true,
          "responding_specialists": [
            {
              "id": "3051",
              "name": "Dr. Priya Shah",
              "specialty": {
                "id": "12",
                "name": "Cardiology"
              }
            }
          ],
          "files": [
            {
              "id": "6d0e4f2a9b",
              "filename": "ecg-2026-11-02.pdf",
              "content_type": "application/pdf",
              "size_bytes": 482113
            }
          ],
          "external_ids": {
            "referral_id": "acme-ref-5521"
          },
          "url": "https://app.theahealth.com/?caseId=a1f3c9e27b4d4e0f9c6b2d8e5f7a1c3b",
          "created_at": "2026-11-04T15:24:11Z",
          "updated_at": "2026-11-04T16:02:45Z",
          "closed_at": null,
          "closed_by": null,
          "first_specialist_response_at": "2026-11-04T16:02:45Z"
        }
      },
      "CaseNew": {
        "summary": "A newly created case",
        "value": {
          "id": "a1f3c9e27b4d4e0f9c6b2d8e5f7a1c3b",
          "practice_id": "152",
          "patient_id": "18342",
          "status": "open",
          "requesting_provider": {
            "id": "2210",
            "name": "Dr. Sam Rivera",
            "email": "sam.rivera@riverbend.example"
          },
          "created_by": null,
          "specialties": [
            {
              "id": "12",
              "name": "Cardiology"
            }
          ],
          "question": "55F with new-onset palpitations; Holter shows frequent PVCs (8% burden). Echo normal. Is a beta-blocker reasonable first-line, and when should she see cardiology in person?",
          "patient_consent_given": true,
          "responding_specialists": [],
          "files": [],
          "external_ids": {
            "referral_id": "acme-ref-5521"
          },
          "url": "https://app.theahealth.com/?caseId=a1f3c9e27b4d4e0f9c6b2d8e5f7a1c3b",
          "created_at": "2026-11-04T15:24:11Z",
          "updated_at": "2026-11-04T15:24:11Z",
          "closed_at": null,
          "closed_by": null,
          "first_specialist_response_at": null
        }
      },
      "CaseClosed": {
        "summary": "A closed case",
        "value": {
          "id": "a1f3c9e27b4d4e0f9c6b2d8e5f7a1c3b",
          "practice_id": "152",
          "patient_id": "18342",
          "status": "closed",
          "requesting_provider": {
            "id": "2210",
            "name": "Dr. Sam Rivera",
            "email": "sam.rivera@riverbend.example"
          },
          "created_by": null,
          "specialties": [
            {
              "id": "12",
              "name": "Cardiology"
            }
          ],
          "question": "55F with new-onset palpitations; Holter shows frequent PVCs (8% burden). Echo normal. Is a beta-blocker reasonable first-line, and when should she see cardiology in person?",
          "patient_consent_given": true,
          "responding_specialists": [
            {
              "id": "3051",
              "name": "Dr. Priya Shah",
              "specialty": {
                "id": "12",
                "name": "Cardiology"
              }
            }
          ],
          "files": [],
          "external_ids": {
            "referral_id": "acme-ref-5521"
          },
          "url": "https://app.theahealth.com/?caseId=a1f3c9e27b4d4e0f9c6b2d8e5f7a1c3b",
          "created_at": "2026-11-04T15:24:11Z",
          "updated_at": "2026-11-04T17:30:00Z",
          "closed_at": "2026-11-04T17:30:00Z",
          "closed_by": {
            "id": "2210",
            "name": "Dr. Sam Rivera",
            "email": "sam.rivera@riverbend.example"
          },
          "first_specialist_response_at": "2026-11-04T16:02:45Z"
        }
      },
      "Message": {
        "summary": "A message posted through the API",
        "value": {
          "id": "88123",
          "case_id": "a1f3c9e27b4d4e0f9c6b2d8e5f7a1c3b",
          "body": "Thanks. Her TSH came back normal at 1.9 if that changes anything.",
          "sender": {
            "type": "provider",
            "id": "2210",
            "name": "Dr. Sam Rivera",
            "via": "Acme EHR"
          },
          "files": [],
          "created_at": "2026-11-04T16:10:02Z"
        }
      },
      "WebhookEndpoint": {
        "summary": "A webhook endpoint",
        "value": {
          "id": "17",
          "url": "https://hooks.acme-ehr.example/thea",
          "description": "Production consult sync",
          "events": [
            "case.message.created",
            "case.closed"
          ],
          "active": true,
          "created_at": "2026-11-04T15:24:11Z"
        }
      }
    },
    "responses": {
      "BadRequest": {
        "description": "The request couldn't be parsed.",
        "headers": {
          "X-Request-Id": {
            "$ref": "#/components/headers/X-Request-Id"
          }
        },
        "content": {
          "application/problem+json": {
            "schema": {
              "$ref": "#/components/schemas/Problem"
            },
            "examples": {
              "malformed-request": {
                "value": {
                  "type": "https://docs.theahealth.com/concepts/errors/#malformed-request",
                  "title": "Malformed request",
                  "status": 400,
                  "detail": "The request body is not valid JSON.",
                  "request_id": "req_8c41f0e2d7b9"
                }
              }
            }
          }
        }
      },
      "Unauthorized": {
        "description": "The partner key or practice key is missing, invalid, or revoked.",
        "headers": {
          "X-Request-Id": {
            "$ref": "#/components/headers/X-Request-Id"
          }
        },
        "content": {
          "application/problem+json": {
            "schema": {
              "$ref": "#/components/schemas/Problem"
            },
            "examples": {
              "unauthenticated": {
                "summary": "Partner key missing or invalid",
                "value": {
                  "type": "https://docs.theahealth.com/concepts/errors/#unauthenticated",
                  "title": "Missing or invalid API key",
                  "status": 401,
                  "detail": "The partner key is missing, revoked, or expired.",
                  "request_id": "req_8c41f0e2d7b9"
                }
              },
              "invalid-practice-key": {
                "summary": "Practice key missing or invalid",
                "value": {
                  "type": "https://docs.theahealth.com/concepts/errors/#invalid-practice-key",
                  "title": "Missing or invalid practice key",
                  "status": 401,
                  "detail": "This request needs a Thea-Practice-Key header issued to your organization.",
                  "request_id": "req_8c41f0e2d7b9"
                }
              }
            }
          }
        }
      },
      "Forbidden": {
        "description": "The key is valid but isn't allowed to do this.",
        "headers": {
          "X-Request-Id": {
            "$ref": "#/components/headers/X-Request-Id"
          }
        },
        "content": {
          "application/problem+json": {
            "schema": {
              "$ref": "#/components/schemas/Problem"
            },
            "examples": {
              "insufficient-scope": {
                "summary": "Key lacks a required scope",
                "value": {
                  "type": "https://docs.theahealth.com/concepts/errors/#insufficient-scope",
                  "title": "Insufficient scope",
                  "status": 403,
                  "detail": "This request needs the cases:write scope for practice 152.",
                  "request_id": "req_8c41f0e2d7b9"
                }
              },
              "practice-not-connected": {
                "summary": "Practice has disconnected",
                "value": {
                  "type": "https://docs.theahealth.com/concepts/errors/#practice-not-connected",
                  "title": "Practice not connected",
                  "status": 403,
                  "detail": "Practice 152 disconnected from your organization, so its practice key no longer works.",
                  "request_id": "req_8c41f0e2d7b9"
                }
              },
              "practice-mismatch": {
                "summary": "practice_id doesn't match the practice key",
                "value": {
                  "type": "https://docs.theahealth.com/concepts/errors/#practice-mismatch",
                  "title": "Practice mismatch",
                  "status": 403,
                  "detail": "This practice key belongs to practice 152, not 977.",
                  "request_id": "req_8c41f0e2d7b9"
                }
              },
              "baa-required": {
                "summary": "Live key used before a BAA is in place",
                "value": {
                  "type": "https://docs.theahealth.com/concepts/errors/#baa-required",
                  "title": "Business associate agreement required",
                  "status": 403,
                  "detail": "Live keys work once Thea has a signed BAA with your organization.",
                  "request_id": "req_8c41f0e2d7b9"
                }
              }
            }
          }
        }
      },
      "NotFound": {
        "description": "The resource doesn't exist in the practice your practice key belongs to.",
        "headers": {
          "X-Request-Id": {
            "$ref": "#/components/headers/X-Request-Id"
          }
        },
        "content": {
          "application/problem+json": {
            "schema": {
              "$ref": "#/components/schemas/Problem"
            },
            "examples": {
              "not-found": {
                "value": {
                  "type": "https://docs.theahealth.com/concepts/errors/#not-found",
                  "title": "Resource not found",
                  "status": 404,
                  "detail": "No case a1f3c9e27b4d4e0f9c6b2d8e5f7a1c3b in practice 152.",
                  "request_id": "req_8c41f0e2d7b9"
                }
              }
            }
          }
        }
      },
      "IdempotencyConflict": {
        "description": "The `Idempotency-Key` was already used with a different request.",
        "headers": {
          "X-Request-Id": {
            "$ref": "#/components/headers/X-Request-Id"
          }
        },
        "content": {
          "application/problem+json": {
            "schema": {
              "$ref": "#/components/schemas/Problem"
            },
            "examples": {
              "idempotency-key-reused": {
                "value": {
                  "type": "https://docs.theahealth.com/concepts/errors/#idempotency-key-reused",
                  "title": "Idempotency key reused",
                  "status": 409,
                  "detail": "This Idempotency-Key was already used with a different request body.",
                  "request_id": "req_8c41f0e2d7b9"
                }
              }
            }
          }
        }
      },
      "ValidationFailed": {
        "description": "The request was well-formed but some fields are invalid.",
        "headers": {
          "X-Request-Id": {
            "$ref": "#/components/headers/X-Request-Id"
          }
        },
        "content": {
          "application/problem+json": {
            "schema": {
              "$ref": "#/components/schemas/ValidationProblem"
            },
            "examples": {
              "validation-error": {
                "value": {
                  "type": "https://docs.theahealth.com/concepts/errors/#validation-error",
                  "title": "Validation failed",
                  "status": 422,
                  "detail": "1 field is invalid.",
                  "request_id": "req_8c41f0e2d7b9",
                  "errors": [
                    {
                      "field": "patient.date_of_birth",
                      "message": "Must be a date in YYYY-MM-DD format."
                    }
                  ]
                }
              }
            }
          }
        }
      },
      "RateLimited": {
        "description": "Too many requests. Wait for `Retry-After` seconds, then retry.",
        "headers": {
          "X-Request-Id": {
            "$ref": "#/components/headers/X-Request-Id"
          },
          "Retry-After": {
            "$ref": "#/components/headers/Retry-After"
          },
          "X-RateLimit-Limit": {
            "$ref": "#/components/headers/X-RateLimit-Limit"
          },
          "X-RateLimit-Remaining": {
            "$ref": "#/components/headers/X-RateLimit-Remaining"
          },
          "X-RateLimit-Reset": {
            "$ref": "#/components/headers/X-RateLimit-Reset"
          }
        },
        "content": {
          "application/problem+json": {
            "schema": {
              "$ref": "#/components/schemas/Problem"
            },
            "examples": {
              "rate-limited": {
                "value": {
                  "type": "https://docs.theahealth.com/concepts/errors/#rate-limited",
                  "title": "Rate limit exceeded",
                  "status": 429,
                  "detail": "Limit of 60 write requests per minute reached for practice 152. Retry in 12 seconds.",
                  "request_id": "req_8c41f0e2d7b9"
                }
              }
            }
          }
        }
      },
      "ServerError": {
        "description": "Something went wrong on Thea's side.",
        "headers": {
          "X-Request-Id": {
            "$ref": "#/components/headers/X-Request-Id"
          }
        },
        "content": {
          "application/problem+json": {
            "schema": {
              "$ref": "#/components/schemas/Problem"
            },
            "examples": {
              "internal-error": {
                "value": {
                  "type": "https://docs.theahealth.com/concepts/errors/#internal-error",
                  "title": "Internal error",
                  "status": 500,
                  "detail": "Something went wrong on our side. Retry with the same Idempotency-Key.",
                  "request_id": "req_8c41f0e2d7b9"
                }
              }
            }
          }
        }
      }
    }
  }
}