Authentication
Authentication Flow
-
To authenticate with the Heidi API and use the widget, you will need to generate a JSON Web Token (JWT) using your API key.
-
The API key provided is unique to your EHR system.
-
Heidi will use your API key in combination with your EHR User UID and email address to generate a unique "Heidi API User" account for the EHR User making the request.
Authenticate with the API
Method: GET
Path: /jwt
Description: Get a JWT token for authenticating with the API.
Request
GET /jwt?email=test@heidihealth.com&third_party_internal_id=123
Heidi-Api-Key: <heidi_api_key>Response
{
"token": "JWT_TOKEN",
"expiration_time": "2024-08-01T00:00:00.000Z"
}Link to a Heidi Account
Method: POST
Path: /users/linked-account
Description: Linked an API user to a Heidi account.
Request
POST /users/linked-account
Authorization: Bearer <your_token>
Content-Type: application/json
{
"kinde_user_id": "kp_xxx",
}Response
{
"account": {
"_id": "690d2e568252d4ba7136bcf5",
"user_id": "kp_c2487b8e79e3413e8cd590ec43d41c9a",
"ehr_provider": "Heidi",
"ehr_email": "test@heidi.com",
"ehr_user_id": "123",
"deleted_at": null,
"created_at": "2025-11-06T23:25:10.127632",
"updated_at": "2025-11-06T23:25:10.127634"
}
}If a user is already linked to a Heidi account, the following error will be returned:
{
"detail": {
"msg": "Heidi account with user_id kp_xxx already linked to an OpenAPI User",
"type": "OpenAPIHeidiAccountAlreadyLinkedError"
}
}Unlink a Heidi Account
Method: DELETE
Path: /users/linked-account:unlink
Description: Remove a linked Heidi account from an API user.
Request
DELETE /users/linked-account:unlink
Authorization: Bearer <your_token>Response
{
"is_success": true
}Checking link status
Method: GET
Path: /users/linked-account/access
Description: Check if an API user is linked to a Heidi account.
Request
GET /users/linked-account/access
Authorization: Bearer <your_token>Response
{
"is_valid": true,
"is_linked": true,
"account": {
"user_id": "Heidi User ID",
"ehr_provider": "Your EHR Provider",
"ehr_email": "The email provided with the API key",
"ehr_user_id": "The user ID provided with the API key"
},
"heidi_email": "The users' email",
"linked_account_plan": "The users' Heidi plan"
}In the response above, if is_linked is false, the user is not linked to a Heidi account. If is_linked is true, the user is linked to a Heidi account.
Team Management
The Team Management APIs allow you to retrieve information about teams and organization members.
List Joined Teams
Method: GET
Path: /team
Description: List all teams that the authenticated user has joined.
Request
GET /team
Authorization: Bearer <your_token>Response
{
"joined_teams": [
{
"team_id": "5eb7cf5a86d9755df3a6c593",
"team_name": "My Team",
"user_role": "ADMIN",
"free_trial_end_date": "2024-12-31T00:00:00.000Z",
"joined_at": "2024-01-01T00:00:00.000Z",
"member_count": 10
}
]
}Response Fields
| Field | Type | Description |
|---|---|---|
team_id | string | Unique identifier for the team |
team_name | string | Display name of the team |
user_role | string | User's role in the team (ADMIN or MEMBER) |
free_trial_end_date | string | ISO 8601 timestamp of when free trial ends (nullable) |
joined_at | string | ISO 8601 timestamp of when user joined (nullable) |
member_count | integer | Number of members in the team (nullable) |
Get Team Details
Method: GET
Path: /team/{team_id}
Description: Get detailed information about a specific team.
Request
GET /team/5eb7cf5a86d9755df3a6c593
Authorization: Bearer <your_token>Path Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
team_id | string | Yes | The unique identifier of the team |
Response
{
"team": {
"id": "5eb7cf5a86d9755df3a6c593",
"name": "My Team",
"country": "AU",
"is_subscribed": true,
"created_at": "2024-01-01T00:00:00.000Z",
"updated_at": "2024-06-01T00:00:00.000Z",
"is_stripe_managed": true,
"charge_future_members": true,
"allow_to_add_team_members": true,
"admin_session_review_toggle": false,
"subscribed_plan": "PRO",
"tier": "PRO",
"plan_cycle": "MONTHLY",
"plan_start_date": "2024-01-01T00:00:00.000Z",
"plan_end_date": "2025-01-01T00:00:00.000Z",
"require_patient_consent": false,
"org_id": "5eb7cf5a86d9755df3a6c594",
"org_name": "My Organization"
}
}Response Fields
| Field | Type | Description |
|---|---|---|
id | string | Unique identifier for the team |
name | string | Team display name |
country | string | Country code for the team |
is_subscribed | boolean | Whether the team has an active subscription |
created_at | string | ISO 8601 timestamp of team creation |
updated_at | string | ISO 8601 timestamp of last update |
is_stripe_managed | boolean | Whether billing is managed through Stripe |
charge_future_members | boolean | Whether new members are automatically charged |
allow_to_add_team_members | boolean | Whether adding new members is allowed |
admin_session_review_toggle | boolean | Whether admin session review is enabled |
subscribed_plan | string | Current subscription plan tier |
tier | string | Current tier level |
plan_cycle | string | Billing cycle (MONTHLY or YEARLY) |
plan_start_date | string | ISO 8601 timestamp of plan start |
plan_end_date | string | ISO 8601 timestamp of plan end |
require_patient_consent | boolean | Whether patient consent is required |
org_id | string | Organization ID (if part of an organization) |
org_name | string | Organization name (if part of an organization) |
List Organization Members
Method: GET
Path: /organization/{organization_id}/members
Description: List members of an organization with pagination and filtering options.
Request
GET /organization/5eb7cf5a86d9755df3a6c593/members?page=1&page_size=10
Authorization: Bearer <your_token>Path Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
organization_id | string | Yes | The unique identifier of the organization |
Query Parameters
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
page | integer | No | 1 | Page number (minimum: 1) |
page_size | integer | No | 10 | Number of results per page (1-200) |
order_by | string | No | name | Field to sort by: name, email, specialty, role, or team |
is_descending | boolean | No | false | Sort in descending order |
search | string | No | - | Search term to filter members |
role_ids | array | No | - | Filter by role IDs |
team_ids | array | No | - | Filter by team IDs |
specialty_ids | array | No | - | Filter by specialty IDs |
show_pending_invitations_only | boolean | No | false | Show only pending invitations |
Response
{
"members": [
{
"id": "5eb7cf5a86d9755df3a6c593",
"team_id": "5eb7cf5a86d9755df3a6c594",
"team_name": "My Team",
"user_role": "MEMBER",
"kinde_user_id": "kp_xxx",
"name": "John Doe",
"email": "john.doe@example.com",
"specialty": "General Practice",
"specialty_id": "5eb7cf5a86d9755df3a6c596",
"country": "AU",
"invitation_accepted": true,
"is_team_owner": false,
"invitation_expire_at": null
}
],
"total": 50,
"page": 1,
"page_size": 10
}Response Fields
| Field | Type | Description |
|---|---|---|
members | array | List of organization members |
total | integer | Total number of members |
page | integer | Current page number |
page_size | integer | Number of members per page |
Member Object Fields
| Field | Type | Description |
|---|---|---|
id | string | Unique identifier for the team relation/invitation |
team_id | string | Team ID the member belongs to |
team_name | string | Name of the team |
user_role | string | User's role (ADMIN or MEMBER) |
kinde_user_id | string | User's Kinde ID |
name | string | Member's display name |
email | string | Member's email address |
specialty | string | Member's medical specialty |
specialty_id | string | Specialty ID (nullable) |
country | string | Member's country code (nullable) |
invitation_accepted | boolean | Whether the invitation has been accepted |
is_team_owner | boolean | Whether the member is the team owner |
invitation_expire_at | string | ISO 8601 timestamp of invitation expiry (nullable) |