openapi: 3.1.0
info:
title: External API
x-logo:
url: "https://storage.googleapis.com/ritten-ops-public-logos/rittenBanner"
backgroundColor: "#FFFFFF"
altText: "Ritten Logo"
description: |
For Ritten Integrating Partners
## Authentication
- Request an access token with your provided integration credentials (`client_id` and `client_secret`) by calling our token endpoint:
```bash
curl https://api.ritten.io/v1/oauth/token \
-X POST \
-H 'content-type: application/json' \
-d '{"client_id":"${client_id}","client_secret":"${client_secret}","audience":"https://external-api.ritten.io","grant_type":"client_credentials"}'
```
- Take the `access_token` from the response and use that as the `Bearer` token in your requests to our API.
- Tokens are long-lived (24 hours / `expires_in: 86400`). The token endpoint also caches server-side, so rapid repeat calls won't hit Auth0 — but feel free to cache the access_token locally if you prefer.
- The token endpoint itself does not require a Bearer token; the `client_secret` in the body is the authentication.
> **Note:** When working in non-production environments, the API endpoints (and `audience` value) will be different.
> For example, in the `beta` environment, the token endpoint is `https://api.beta.ritten.io/v1/oauth/token`
> and the audience is `https://external-api.beta.ritten.io`.
## Tenant Header
- Make sure to add the tenant ID to the header of every request. This is the Ritten Clinic instance the request will target. Example:
```
X-Ritten-Tenant: ritclinic
```
## Rate Limiting
Two layers of rate limiting apply: per-request limits on API calls, and per-app limits on token minting.
### API request rate limit
Applied to authenticated API calls (everything except `/v1/oauth/token`):
- 50 requests per second sustained rate
- 100 requests burst allowance
You can make up to 100 requests in a short burst, but over time your average must stay at or below 50 requests per second. Think of it as a bucket that holds 100 tokens and refills at 50 tokens per second. Each request consumes one token. You'll receive a `429 Too Many Requests` response when this is triggered.
### Token mint quota (Auth0)
A separate per-application limit on how often you can mint new access tokens:
- 2 mints per hour
- 3 mints per day
These limits are applied at the Auth0 layer and count mints across both the legacy direct path and the cached `/v1/oauth/token` endpoint combined. **The cached endpoint is designed so that one mint per day is sufficient for any traffic volume** — the proxy serves all subsequent requests from the cached token. If you migrate to the cached endpoint, you will not notice these limits.
Token mint quotas currently apply to all newly-provisioned integrator clients. They will be rolled out to existing clients on a separate schedule, and you will be contacted before that change applies to you.
version: 1.0.0
servers:
- url: https://api.ritten.io/v1
tags:
- name: oauth
description: |
OAuth 2.0 token endpoint for obtaining access tokens. This is the
recommended way to authenticate with the Ritten External API.
- name: patients
- name: contacts
- name: users
- name: calendar
- name: insurance
- name: encounters
description: |
Endpoints for creating clinical encounters (visits) and discovering the
encounter types they are created from.
- name: forms
description: |
Endpoints for accessing form definitions and form-related data.
- name: insights
description: |
Ritten provides insights and reporting endpoints for analytics and data export.
These endpoints return various reports about admissions, discharges, census, billing, and CRM data.
Most endpoints support CSV export via the `csv=true` query parameter.
- name: cases
description: |
Endpoints for accessing CRM cases (admissions pipeline).
- name: facilities
description: |
Endpoints for accessing clinic facilities (service locations).
- name: programs
description: |
Endpoints for creating, accessing, and managing clinic programs.
- name: organizations
description: |
Endpoints for accessing CRM organizations and organization members. Requires CRM to be available for the target clinic (`X-Ritten-Tenant`). Integrations must be explicitly provisioned by Ritten for organization access.
- name: tasks
description: |
Endpoints for accessing clinic tasks. Tasks linked to a client are only returned when the integration is authorized to read that client.
- name: webhooks
description: |
Ritten provides webhooks for various events that occur in the system.
Integrating partners can subscribe to these events to receive real-time updates.
Webhooks sent from Ritten are POST requests with a JSON body containing the event `type` and associated `data`.
The schema for the `data` object will vary depending on the event type.
```
{
"type": "patient.admit",
"data": {
"patientId": "182c2e54-3494-4b85-aba5-038cf539d5bf",
"programId": "9a5e64b0-0a73-4cb5-ab32-44fea16da4e1"
}
}
```
paths:
/oauth/token:
post:
tags:
- oauth
summary: Obtain an access token (OAuth 2.0 client_credentials)
description: |
Exchanges integrator credentials for a 24-hour access token to use as a `Bearer`
token on subsequent API calls. This endpoint is **unauthenticated** at the gateway
layer — your `client_secret` in the request body is the authentication. Ritten
forwards the credentials to Auth0, validates the response, and caches the resulting
token server-side so repeated calls do not consume your Auth0 mint quota.
The response shape mirrors the OAuth 2.0 / Auth0 `/oauth/token` response so existing
OAuth2 client libraries work without modification.
**Mint quota interaction:** the per-app token mint quota (2/hour, 3/day) counts
mints actually performed against Auth0. Because this endpoint caches server-side,
repeated calls within a 24-hour window typically result in zero additional Auth0
mints — so calling here is far cheaper against your quota than calling Auth0
directly. See the Authentication and Rate Limiting sections above.
operationId: postOAuthToken
security: []
requestBody:
required: true
content:
application/json:
schema:
$ref: "#/components/schemas/OAuthTokenRequest"
application/x-www-form-urlencoded:
schema:
$ref: "#/components/schemas/OAuthTokenRequest"
responses:
200:
description: Access token issued.
content:
application/json:
schema:
$ref: "#/components/schemas/OAuthTokenResponse"
400:
description: |
Malformed request. The `error` field will be one of `invalid_request`,
`unsupported_grant_type`, or `invalid_audience`.
content:
application/json:
schema:
$ref: "#/components/schemas/OAuthErrorResponse"
401:
description: |
Auth0 rejected the supplied credentials (`error: invalid_client` or similar).
content:
application/json:
schema:
$ref: "#/components/schemas/OAuthErrorResponse"
429:
description: |
Rate limit exceeded. Either the per-IP request rate limit on this endpoint,
or the per-app Auth0 mint quota (2/hour, 3/day) has been reached. Retry
after a short delay. If 429s persist, inspect whether you are hitting
per-IP request limits vs. triggering fresh Auth0 mints, then reach out
to Ritten for support.
content:
application/json:
schema:
$ref: "#/components/schemas/OAuthErrorResponse"
502:
description: |
Auth0 was unreachable or returned a 5xx error. Retry — Ritten does not
cache failed responses.
content:
application/json:
schema:
$ref: "#/components/schemas/OAuthErrorResponse"
/patients:
get:
tags:
- patients
summary: List patients in a clinic
description: Lists patients in a clinic
operationId: listPatients
parameters:
- name: programStatus
in: query
description: Patient Program status filter
required: true
schema:
type: string
enum:
- prospective
- active
- former
- name: limit
in: query
description: How many patients to return at one time (max 20).
schema:
maximum: 20
type: integer
format: int64
- name: offset
in: query
description:
How many patients to skip before returning the limit number of
patients. Use this to page.
schema:
type: integer
format: int64
responses:
200:
description: success
content:
application/json:
schema:
$ref: "#/components/schemas/ListPatients"
post:
tags:
- patients
summary: Create a new patient
description: Creates a new patient record (this will generate an MRN and put the patient in 'prospective' status)
operationId: createPatient
requestBody:
content:
application/json:
schema:
required:
- name
properties:
name:
$ref: "#/components/schemas/PatientName"
dob:
type: string
description: Date of birth (YYYY-MM-DD)
example: "1990-02-23"
externalId:
type: string
description: An external identifier for the patient (e.g. an ID from a third-party system)
responses:
200:
description: success
content:
application/json:
schema:
$ref: "#/components/schemas/IDSchema"
/patients/{id}:
get:
tags:
- patients
summary: Retrieve a patient by ID
description: Returns a single patient
operationId: getPatientById
parameters:
- name: id
in: path
description: ID of patient to return
required: true
schema:
type: string
responses:
200:
description: success
content:
application/json:
schema:
$ref: "#/components/schemas/PatientDetail"
400:
description: Invalid ID supplied
404:
description: Patient not found
patch:
tags:
- patients
summary: Update a patient by ID
description: |
Update a single patient. Returns the updated patient.
Omitting a top-level field in the request body will leave it unchanged.
operationId: patchPatient
parameters:
- name: id
in: path
description: ID of patient to update
required: true
schema:
type: string
requestBody:
content:
application/json:
schema:
properties:
name:
allOf:
- $ref: "#/components/schemas/PatientName"
description: The patient's name. Omitted nested fields are left unchanged.
dob:
type: string
description: Date of birth (YYYY-MM-DD)
example: "1990-02-23"
ssn:
type: string
writeOnly: true
description: Full Social Security number. Accepted with or without dashes and never returned in patient responses.
examples:
- "123-45-6789"
- "123456789"
demographics:
$ref: "#/components/schemas/PatientDemographics"
emails:
type: array
items:
type: string
example: "john@example.com"
phones:
type: array
items:
type: string
example: "+15555555555"
addresses:
type: array
items:
$ref: "#/components/schemas/Address"
description: The patient's addresses. To update an existing address, include the address ID.
referrals:
type: array
items:
$ref: "#/components/schemas/Referral"
description: The patient's referrals. To update an existing referral, include the referral ID.
insurance:
type: array
items:
$ref: "#/components/schemas/Insurance"
description: The patient's insurance information. To update an existing insurance record, include the insurance ID.
externalId:
type: string
description: An external identifier for the patient (e.g. an ID from a third-party system)
responses:
200:
description: success
content:
application/json:
schema:
$ref: "#/components/schemas/PatientDetail"
400:
description: Invalid ID or payload supplied
404:
description: Patient not found
/patients/{id}/vitals:
post:
tags:
- patients
summary: Record patient vitals
description: |
Records a single vital observation for the patient. Units are fixed by observation and measurement type;
do not include units in the request body.
operationId: postPatientVitals
parameters:
- name: id
in: path
description: ID of patient to record vitals for
required: true
schema:
type: string
format: uuid
requestBody:
required: true
content:
application/json:
schema:
$ref: "#/components/schemas/PostPatientVital"
examples:
temperature:
summary: Temperature in Fahrenheit
value:
type: temperature
measurements:
- value: 98.6
timestamp: "2026-05-27T15:30:00Z"
heart_rate:
summary: Heart rate in beats per minute
value:
type: heart_rate
measurements:
- value: 72
timestamp: "2026-05-27T15:30:00Z"
respiration_rate:
summary: Respiration rate in breaths per minute
value:
type: respiration_rate
measurements:
- value: 16
timestamp: "2026-05-27T15:30:00Z"
oxygen_saturation:
summary: Oxygen saturation percentage
value:
type: oxygen_saturation
measurements:
- value: 98
timestamp: "2026-05-27T15:30:00Z"
blood_pressure:
summary: Blood pressure in mmHg
value:
type: blood_pressure
measurements:
- type: systolic
value: 120
- type: diastolic
value: 80
timestamp: "2026-05-27T15:30:00Z"
glucose:
summary: Glucose in mg/dL
value:
type: glucose
measurements:
- value: 110
timestamp: "2026-05-27T15:30:00Z"
blood_alcohol_concentration:
summary: Blood alcohol concentration percentage
value:
type: blood_alcohol_concentration
measurements:
- value: 0.02
timestamp: "2026-05-27T15:30:00Z"
height_weight_height_only:
summary: Height in total inches
value:
type: height_weight
measurements:
- type: height
value: 70
timestamp: "2026-05-27T15:30:00Z"
height_weight_weight_only:
summary: Weight in pounds
value:
type: height_weight
measurements:
- type: weight
value: 180
timestamp: "2026-05-27T15:30:00Z"
height_weight_both:
summary: Height in total inches and weight in pounds
value:
type: height_weight
measurements:
- type: height
value: 70
- type: weight
value: 180
timestamp: "2026-05-27T15:30:00Z"
responses:
204:
description: Vital recorded successfully
400:
description: Invalid ID or payload supplied
404:
description: Patient not found
/patients/{id}/forms:
post:
tags:
- forms
summary: Create a form on a patient chart and submit field values
description: |
Creates a form instance from a pre-configured form definition (resolved by its label) on the
patient's chart and writes all provided field values in a single atomic call — on any error,
nothing is written. Use `GET /forms/definitions` to discover form, section, and field labels.
Every ingested form attaches to an existing encounter (visit) on the patient's chart: create
the encounter first, then submit form data with its `encounterId`. The encounter must belong
to the patient.
Values are keyed by field definition label and must match the field's type: strings for
`text` and `text_paragraph`, `YYYY-MM-DD` or RFC 3339 strings for `date`, numbers for
`number_float`, a configured choice string for single-choice fields (scale answers are string
integers, e.g. `"2"`), and arrays of choice strings for multi-choice fields. Grid-matrix
values are row-keyed objects whose per-row shape depends on the sub-type: `grid_matrix_text`
and `grid_matrix_number` map each row index to a column-keyed object of cells
(`{"0": {"1": "cell"}}`), `grid_matrix_choice` maps each row index to a single integer column
index (`{"0": 1}`), and `grid_matrix_multi_choice` maps each row index to an array of integer
column indices (`{"0": [0, 1]}`). A JSON `null` clears a field (including any default).
Display-only, calculated, and chart-backed (`roobi_custom_field`) fields cannot be written.
Omit `values` to create an empty form.
Submitted field labels must be unambiguous within the form definition: if a label matches more
than one field on the created form, the request fails with a 400.
The created form behaves exactly like one created on the encounter in-app: it arrives
unsigned with the definition's signature requirements and signature actions, signers are
notified, due-forms scheduling applies, and the encounter's documentation status is
recalculated. Requests are not idempotent: retrying a successful call creates a second form
instance.
operationId: postPatientForm
parameters:
- name: id
in: path
description: ID of the patient to create the form for
required: true
schema:
type: string
format: uuid
requestBody:
required: true
content:
application/json:
schema:
$ref: "#/components/schemas/PostPatientForm"
examples:
phq9:
summary: Submit a PHQ-9 assessment
value:
formDefinitionLabel: phq9_intake_form
encounterId: "6a6f8ab1-38b0-4c96-9f39-2a37e2f4a111"
values:
roobi_depression_phq9_q1: "2"
roobi_depression_phq9_q2: "3"
reason_for_visit: referral
empty_form:
summary: Create an empty form (no values)
value:
formDefinitionLabel: weekly_progress_note
encounterId: "6a6f8ab1-38b0-4c96-9f39-2a37e2f4a111"
responses:
200:
description: Form created and all values written
content:
application/json:
schema:
$ref: "#/components/schemas/PatientForm"
400:
description: Invalid payload — unknown or ambiguous field label, value invalid for the field's type, or unsupported form definition
404:
description: Patient, form definition label, or encounter not found
/patients/{id}/relationships:
get:
tags:
- patients
summary: List a patient's relationships
description: Returns a list of relationships for a patient
operationId: listPatientRelationships
parameters:
- name: id
in: path
description: ID of patient to return relationships for
required: true
schema:
type: string
responses:
200:
description: success
content:
application/json:
schema:
type: array
items:
$ref: "#/components/schemas/PatientRelationship"
400:
description: Invalid ID supplied
404:
description: Patient not found
post:
tags:
- patients
summary: Create a new patient relationship
description: Creates a new relationship between two patients
operationId: createPatientRelationship
parameters:
- name: id
in: path
description: ID of patient to create a relationship for
required: true
schema:
type: string
requestBody:
content:
application/json:
schema:
required:
- personId
- type
properties:
personId:
type: string
description: ID of the person to create a relationship with (patient or contact)
type:
type: string
description: The type of relationship
isEmergencyContact:
type: boolean
isGuarantor:
type: boolean
isGuardian:
type: boolean
responses:
200:
description: success
content:
application/json:
schema:
$ref: "#/components/schemas/PatientRelationship"
400:
description: Invalid payload supplied
/patients/{id}/relationships/{relationshipId}:
patch:
tags:
- patients
summary: Update a patient relationship
description: |
Update a single patient relationship. Returns the updated relationship.
Omitting a top-level field in the request body will leave it unchanged.
operationId: patchPatientRelationship
parameters:
- name: id
in: path
description: ID of patient to update a relationship for
required: true
schema:
type: string
- name: relationshipId
in: path
description: ID of relationship to update
required: true
schema:
type: string
requestBody:
content:
application/json:
schema:
properties:
type:
type: string
description: The type of relationship
isEmergencyContact:
type: boolean
isGuarantor:
type: boolean
isGuardian:
type: boolean
responses:
200:
description: success
content:
application/json:
schema:
$ref: "#/components/schemas/PatientRelationship"
400:
description: Invalid ID or payload supplied
404:
description: Relationship not found
delete:
tags:
- patients
summary: Delete a patient relationship
description: Deletes a patient relationship by ID
operationId: deletePatientRelationship
parameters:
- name: id
in: path
description: ID of patient to delete a relationship for
required: true
schema:
type: string
- name: relationshipId
in: path
description: ID of relationship to delete
required: true
schema:
type: string
responses:
200:
description: success
/patients/{id}/attachments:
post:
tags:
- patients
summary: Attach a document to a patient chart
description:
Uploads a file to Ritten and then attaches it as a document on the
patient chart which lives under the Attachments section in the Platform
operationId: attachDocument
parameters:
- name: id
in: path
description: ID of patient
required: true
schema:
type: string
requestBody:
content:
multipart/form-data:
schema:
required:
- file
- title
- type
properties:
type:
description: The Ritten document type. This can also include any custom types defined by the clinic.
$ref: "#/components/schemas/DocumentTypeEnum"
title:
type: string
description: The display title of the document
file:
type: string
description: The file upload
format: binary
required: true
responses:
200:
description: success
/patients/{id}/attachments/{attachmentId}:
patch:
tags:
- patients
summary: Update a document on a patient chart
description: Update the title and/or type of an existing document on a patient chart. Omitting a field will leave it unchanged.
operationId: updateDocument
parameters:
- name: id
in: path
description: ID of patient
required: true
schema:
type: string
- name: attachmentId
in: path
description: ID of attachment to update
required: true
schema:
type: string
requestBody:
content:
application/json:
schema:
properties:
type:
description: The Ritten document type. This can also include any custom types defined by the clinic.
$ref: "#/components/schemas/DocumentTypeEnum"
title:
type: string
description: The display title of the document
responses:
200:
description: success
/patients/external/{externalId}:
get:
tags:
- patients
summary: Retrieve a patient by external ID
description: Returns a single patient with the given external ID
operationId: getPatientByExternalId
parameters:
- name: externalId
in: path
description: External ID of patient to return
required: true
schema:
type: string
responses:
200:
description: success
content:
application/json:
schema:
$ref: "#/components/schemas/PatientDetail"
404:
description: Patient not found
/contacts:
get:
tags:
- contacts
summary: List contacts in a clinic
description: Lists contacts in a clinic
operationId: listContacts
parameters:
- name: limit
in: query
description: How many contacts to return at one time (max 20).
schema:
maximum: 20
type: integer
format: int64
- name: offset
in: query
description:
How many contacts to skip before returning the limit number of
contacts. Use this to page.
schema:
type: integer
format: int64
responses:
200:
description: success
content:
application/json:
schema:
$ref: "#/components/schemas/ListContacts"
post:
tags:
- contacts
summary: Create a new contact
description: Creates a new contact record
operationId: createContact
requestBody:
content:
application/json:
schema:
required:
- first
- last
properties:
first:
type: string
description: The contact's first name
example: John
middle:
type: string
description: The contact's middle name
last:
type: string
description: The contact's last name
example: Doe
dob:
type: string
description: Date of birth (YYYY-MM-DD)
example: "1990-02-23"
address:
$ref: "#/components/schemas/Address"
contactPoints:
type: array
items:
$ref: "#/components/schemas/ContactPoint"
responses:
200:
description: success
content:
application/json:
schema:
$ref: "#/components/schemas/ContactDetail"
400:
description: Invalid payload supplied
/contacts/{id}:
get:
tags:
- contacts
summary: Retrieve a contact by ID
description: Returns a single contact
operationId: getContactById
parameters:
- name: id
in: path
description: ID of contact to return
required: true
schema:
type: string
responses:
200:
description: success
content:
application/json:
schema:
$ref: "#/components/schemas/ContactDetail"
400:
description: Invalid ID supplied
404:
description: Contact not found
patch:
tags:
- contacts
summary: Update a contact by ID
description: |
Update a single contact. Returns the updated contact.
Omitting a top-level field in the request body will leave it unchanged.
operationId: patchContact
parameters:
- name: id
in: path
description: ID of contact to update
required: true
schema:
type: string
requestBody:
content:
application/json:
schema:
properties:
first:
type: string
description: The contact's first name
example: John
middle:
type: string
description: The contact's middle name
last:
type: string
description: The contact's last name
example: Doe
# --- truncated at 32 KB (186 KB total) ---
# Full source: https://raw.githubusercontent.com/api-evangelist/ritten/refs/heads/main/openapi/ritten-external-api-openapi.yaml