Ritten External API

The Ritten External API is the public REST contract for Ritten integrating partners. It covers patients (create, read, patch, vitals, forms, relationships, attachments, external-id lookup), contacts, CRM cases in the admissions pipeline, programs, facilities, organizations, tasks, clinical encounters and encounter types, form definitions, insurance payers, users/teams and calendar events, plus a 17-operation insights family for census, admissions, discharges, average length of stay, care teams, attendance, event audit and form outcomes - most of which also export CSV via ?csv=true. Six webhook events cover patient admit/transfer/discharge/created and case created/status-update. Every request requires an OAuth 2.0 client_credentials bearer token and an X-Ritten-Tenant header identifying the clinic instance.

Documentation

Specifications

Other Resources

OpenAPI Specification

ritten-external-api-openapi.yaml Raw ↑
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