Skip to content

Practices and connections

Your organization is a partner on Thea. A partner works with one or more practices (clinics). Each practice connects to your organization once, and a Thea practice admin approves it, because the practice admin controls which clinicians have Thea seats and what the practice pays.

Connecting gives you a practice key for that practice. Every request about the practice’s data uses it, together with your partner key. See Authentication.

  1. Send the admin to Thea. From your product (for example, a “Connect Thea” button in practice settings), redirect the practice admin to:

    https://app.theahealth.com/connect/{partner_slug}?redirect_uri={redirect_uri}&state={state}
    • partner_slug is your organization’s identifier, which Thea gives you with your partner key.
    • redirect_uri must exactly match one you’ve registered with Thea.
    • state is an opaque value you generate per attempt. Check it when the admin comes back, to block forged callbacks.

    Use https://dev.theahealth.com/connect/… in the sandbox.

  2. The admin approves. They sign in to Thea (or sign the practice up, including a payment method), review the scopes your organization agreed with Thea, and approve the connection.

  3. Thea redirects back to {redirect_uri}?code={code}&state={state}. Codes are single-use and expire after 10 minutes.

  4. Exchange the code from your server, with your partner key:

    Terminal window
    curl -X POST "$THEA_API/connect/token" \
    -H "Authorization: Bearer $THEA_PARTNER_KEY" \
    -H "Content-Type: application/json" \
    -d '{ "grant_type": "authorization_code", "code": "cc_9Hd2kQ7xLm", "redirect_uri": "https://app.acme-ehr.example/integrations/thea/callback" }'
    Response
    {
    "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"
    }

    Store practice_key securely now: it’s returned only once.

A code that’s expired, already used, or issued for a different redirect_uri returns 400 invalid-grant. Send the admin through the flow again.

Mapping your practices and locations to Thea practices is up to you. One of your multi-location organizations may be one Thea practice, or several.

A practice key only reaches its own practice:

  • Lists (GET /patients, GET /cases) return that practice’s records. Make one call per practice key.
  • Retrieving a patient or case from another practice returns 404 not-found, the same as one that doesn’t exist, so IDs from other practices reveal nothing.
  • practice_id is optional in request bodies. If you send it, it must match the key’s practice, or the request fails with 403 practice-mismatch. The same applies to practice_id in paths such as /practices/{practice_id}/providers.

Store each practice’s practice_id together with its practice key when you exchange the code. Practice IDs don’t change.

A connection can end three ways:

  • The practice admin disconnects your organization in Thea.
  • Your server calls DELETE /practices/{practice_id}/connection.
  • Thea disconnects it, for example at the practice’s request.

Either way the practice key stops working immediately, and Thea sends a practice.disconnected webhook. Then:

  • Open cases carry on in Thea, and clinicians finish them there. Cases closed after the disconnect don’t reach you.
  • Stop offering eConsults for that practice, and clear its roster mapping.
  • Records you created stay in Thea.

Requests with a disconnected practice’s key return 403 practice-not-connected.

Webhook endpoints belong to your organization, not to one practice. An endpoint receives events from every connected practice; each event includes its practice_id, so you can pick the right practice key when you fetch details.