> ## Documentation Index
> Fetch the complete documentation index at: https://docs.usecobalt.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Operation Events

Operation events fire when Cobalt performs an RPA action inside your EHR system. They confirm the success or failure of operations Cobalt initiated on your behalf — creating appointments, adding patient insurance, uploading notes, creating tasks, and so on.

<Note>
  Some events listed here (`appointment.created`, `appointment.updated`, `patient.updated`) also fire from EHR sync when staff make changes directly in the EHR. See [Sync Events](/docs/webhooks/sync-events) for those.
</Note>

## Payload Envelope

Every operation event uses this envelope:

```json theme={null}
{
  "id": "550e8400-e29b-41d4-a716-446655440000",
  "access_token_reference_id": "your-reference-id",
  "object": "event",
  "created": "2025-10-21T14:30:00.000Z",
  "type": "appointment.created",
  "job_id": "12345",
  "data": {
    // Event-specific payload
  }
}
```

| Field                       | Type   | Description                                    |
| --------------------------- | ------ | ---------------------------------------------- |
| `id`                        | string | Unique identifier for this event               |
| `access_token_reference_id` | string | Your reference ID associated with this webhook |
| `object`                    | string | Always `"event"`                               |
| `created`                   | string | ISO 8601 timestamp when the event was created  |
| `type`                      | string | The event type                                 |
| `job_id`                    | string | API log ID for tracking the operation          |
| `data`                      | object | Event-specific data payload                    |

## Structured Error Codes

Operation failure events (`appointment.failed`, `patient.failed`, etc.) include **structured error codes** so you can handle different failure scenarios programmatically:

* **Machine-readable error codes** — parse errors programmatically instead of string matching
* **Context-aware descriptions** — error messages include specific details (MRN, provider names)
* **Backward compatibility** — the legacy `failure_reason` field is still included

### Error Response Structure

Failure events include both the legacy `failure_reason` field and a `reasons` array:

```json theme={null}
{
  "type": "appointment.failed",
  "data": {
    "appointment_id": "abc123def456",
    "mrn": "MRN123456",
    "failure_reason": "Patient MRN 'MRN123456' not found. Please update the MRN...",
    "reasons": [
      {
        "code": "PATIENT_NOT_FOUND",
        "description": "Patient MRN 'MRN123456' not found. Please update the MRN using the PATCH /v1/appointments/:id endpoint."
      }
    ]
  }
}
```

* `failure_reason` (string) — concatenated description for backward compatibility
* `reasons` (array) — structured error objects, each with a machine-readable `code` and a human-readable `description`

### Handling Errors Programmatically

```javascript theme={null}
function handleAppointmentFailed(webhookData) {
  const reasons = webhookData.data.reasons || [];

  for (const reason of reasons) {
    switch (reason.code) {
      case 'PATIENT_NOT_FOUND':
        updatePatientMrn(webhookData.data.mrn);
        break;
      case 'PROVIDER_UNAVAILABLE':
        findAlternativeSlots(webhookData.data.appointment_id);
        break;
      case 'INVALID_VISIT_TYPE':
        mapVisitType(webhookData.data.appointment_id);
        break;
      default:
        alertSupport(reason);
    }
  }
}
```

## Appointment Events

<AccordionGroup>
  <Accordion title="appointment.created" icon="calendar-plus">
    Triggered when Cobalt successfully creates an appointment in your EHR via an RPA action.

    **Payload Example:**

    ```json theme={null}
    {
      "id": "759df7ac-2688-44a9-926c-58d36376b412",
      "access_token_reference_id": "clinic_1",
      "object": "event",
      "created": "2025-07-28T16:05:48.929Z",
      "type": "appointment.created",
      "job_id": "12345",
      "data": {
        "appointment_id": "c5c0e613fc7d68a1c01ef4160483a0b6",
        "mrn": "44235",
        "date_time": "2025-07-28T16:00:00.000Z",
        "timezone": "America/New_York",
        "provider_id": "defe69ae-f363-4797-87bd-1c70acb52b21",
        "provider_name": "Dr. Jose Gonzalez",
        "secondary_provider_id": null
      }
    }
    ```

    **Data Fields:**

    | Field                   | Type           | Description                                    |
    | ----------------------- | -------------- | ---------------------------------------------- |
    | `appointment_id`        | string         | Cobalt's unique identifier for the appointment |
    | `mrn`                   | string         | Patient's Medical Record Number                |
    | `date_time`             | string         | Appointment date and time (ISO 8601)           |
    | `timezone`              | string         | Timezone for the appointment                   |
    | `provider_id`           | string         | Primary provider's ID                          |
    | `provider_name`         | string         | Primary provider's name                        |
    | `secondary_provider_id` | string \| null | Secondary provider's ID (if applicable)        |
  </Accordion>

  <Accordion title="appointment.updated" icon="calendar-pen">
    Triggered when Cobalt successfully updates an appointment in your EHR via an RPA action.

    **Payload Example:**

    ```json theme={null}
    {
      "id": "508c368e7de13b40f9397eec966d0329",
      "access_token_reference_id": "clinic_1",
      "object": "event",
      "created": "2025-07-28T08:50:42.491-07:00",
      "type": "appointment.updated",
      "job_id": "12345",
      "data": {
        "id": "0551c88b2249945151f967525ee3f7b4",
        "ehr_appointment_id": "482129",
        "patient_mrn": "804",
        "patient_name": "John Doe",
        "datetime": "2025-07-28T16:00:00.000Z",
        "duration": 20,
        "location": "2",
        "status": "2",
        "provider_name": "Dr. Michael Denenberg",
        "appointment_type": "FOLLOW-UP",
        "appointment_mode": "office",
        "updated_fields": {
          "status": "2"
        }
      }
    }
    ```

    **Data Fields:**

    | Field                | Type   | Description                                    |
    | -------------------- | ------ | ---------------------------------------------- |
    | `id`                 | string | Cobalt's unique identifier for the appointment |
    | `ehr_appointment_id` | string | The EHR's native appointment ID                |
    | `patient_mrn`        | string | Patient's Medical Record Number                |
    | `patient_name`       | string | Patient's full name                            |
    | `datetime`           | string | Appointment date and time (ISO 8601)           |
    | `duration`           | number | Appointment duration in minutes                |
    | `location`           | string | Appointment location/room                      |
    | `status`             | string | Appointment status code                        |
    | `provider_name`      | string | Provider's name                                |
    | `appointment_type`   | string | Type of appointment                            |
    | `appointment_mode`   | string | Mode (e.g., "office", "telehealth")            |
    | `updated_fields`     | object | Fields that changed in this update             |
  </Accordion>

  <Accordion title="appointment.failed" icon="calendar-xmark">
    Triggered when Cobalt fails to create an appointment in your EHR system.

    **Payload Example:**

    ```json theme={null}
    {
      "id": "550e8400-e29b-41d4-a716-446655440000",
      "access_token_reference_id": "clinic_1",
      "object": "event",
      "created": "2025-10-21T14:30:00.000Z",
      "type": "appointment.failed",
      "job_id": "12345",
      "data": {
        "appointment_id": "abc123def456",
        "mrn": "MRN123456",
        "failure_reason": "Patient MRN 'MRN123456' not found. Please update the MRN using the PATCH /v1/appointments/:id endpoint.",
        "reasons": [
          {
            "code": "PATIENT_NOT_FOUND",
            "description": "Patient MRN 'MRN123456' not found. Please update the MRN using the PATCH /v1/appointments/:id endpoint."
          }
        ]
      }
    }
    ```

    **Data Fields:**

    | Field            | Type   | Description                                            |
    | ---------------- | ------ | ------------------------------------------------------ |
    | `appointment_id` | string | Cobalt's identifier for the failed appointment         |
    | `mrn`            | string | Patient's Medical Record Number                        |
    | `failure_reason` | string | Legacy human-readable error message                    |
    | `reasons`        | array  | Structured error objects with `code` and `description` |

    **Common Error Codes:**

    * `PATIENT_NOT_FOUND` — Patient not found in EHR
    * `PROVIDER_NOT_FOUND` — Provider not found or invalid
    * `INVALID_VISIT_TYPE` — Visit type not configured in EHR
    * `LOCATION_NOT_FOUND` — Location/facility not found
    * `TIME_SLOT_UNAVAILABLE` — Requested time slot is not available
    * `PROVIDER_UNAVAILABLE` — Provider unavailable at requested time
    * `DOUBLE_BOOKING_CONFLICT` — Provider's schedule has a conflicting visit or block at the requested time (only returned when `prevent_double_booking: "true"` was set on the appointment)
    * `INVALID_COMPLAINT_TYPE` — Chief complaint type not found
    * `TERMINAL_FAILURE` — Unspecified terminal failure
  </Accordion>

  <Accordion title="appointment.update_failed" icon="calendar-xmark">
    Triggered when Cobalt fails to update an existing appointment in your EHR system.

    **Payload Example:**

    ```json theme={null}
    {
      "id": "550e8400-e29b-41d4-a716-446655440001",
      "access_token_reference_id": "clinic_1",
      "object": "event",
      "created": "2025-10-21T14:30:00.000Z",
      "type": "appointment.update_failed",
      "job_id": "12345",
      "data": {
        "appointment_id": "abc123def456",
        "mrn": "MRN123456",
        "failure_reason": "Appointment not found in EHR. The appointment may have been deleted or moved.",
        "reasons": [
          {
            "code": "APPOINTMENT_NOT_FOUND",
            "description": "Appointment not found in EHR. The appointment may have been deleted or moved."
          }
        ]
      }
    }
    ```

    **Data Fields:**

    | Field            | Type   | Description                                            |
    | ---------------- | ------ | ------------------------------------------------------ |
    | `appointment_id` | string | Cobalt's identifier for the appointment                |
    | `mrn`            | string | Patient's Medical Record Number                        |
    | `failure_reason` | string | Legacy human-readable error message                    |
    | `reasons`        | array  | Structured error objects with `code` and `description` |

    **Common Error Codes:**

    * `APPOINTMENT_NOT_FOUND` — Appointment not found in EHR
    * `SECTION_LOCKED` — Appointment section is locked
    * `INVALID_STATUS` — Invalid status transition
    * `UPDATE_BLOCKED` — Update blocked by EHR rules
    * `CLAIMS_ASSOCIATED` — Claims exist, update not allowed
    * `UPDATE_VERIFICATION_FAILED` — Update verification failed
    * `TERMINAL_FAILURE` — Unspecified terminal failure
  </Accordion>
</AccordionGroup>

## Patient Events

<AccordionGroup>
  <Accordion title="patient.created" icon="user-plus">
    Triggered when Cobalt successfully creates a patient in your EHR system.

    **Payload Example:**

    ```json theme={null}
    {
      "id": "550e8400-e29b-41d4-a716-446655440010",
      "access_token_reference_id": "clinic_1",
      "object": "event",
      "created": "2025-10-21T14:30:00.000Z",
      "type": "patient.created",
      "job_id": "12345",
      "data": {
        "patient_id": "xyz789uvw012",
        "mrn": "MRN123456",
        "first_name": "Jane",
        "last_name": "Smith",
        "date_of_birth": "1985-06-15",
        "sex": "F",
        "email": "jane.smith@example.com",
        "phone": "555-0123"
      }
    }
    ```

    **Data Fields:**

    | Field           | Type   | Description                                |
    | --------------- | ------ | ------------------------------------------ |
    | `patient_id`    | string | Cobalt's unique identifier for the patient |
    | `mrn`           | string | Patient's Medical Record Number            |
    | `first_name`    | string | Patient's first name                       |
    | `last_name`     | string | Patient's last name                        |
    | `date_of_birth` | string | Patient's date of birth (YYYY-MM-DD)       |
    | `sex`           | string | Patient's sex                              |
    | `email`         | string | Patient's email address                    |
    | `phone`         | string | Patient's phone number                     |
  </Accordion>

  <Accordion title="patient.failed" icon="user-xmark">
    Triggered when Cobalt fails to create a patient in your EHR system.

    **Payload Example:**

    ```json theme={null}
    {
      "id": "550e8400-e29b-41d4-a716-446655440002",
      "access_token_reference_id": "clinic_1",
      "object": "event",
      "created": "2025-10-21T14:30:00.000Z",
      "type": "patient.failed",
      "job_id": "12345",
      "data": {
        "patient_id": "xyz789uvw012",
        "failure_reason": "Patient already exists with MRN 'MRN789456'.",
        "reasons": [
          {
            "code": "DUPLICATE_PATIENT",
            "description": "Patient already exists with MRN 'MRN789456'. Please update the patient information using the PATCH /v1/patients/:id endpoint or search using GET /v1/patients."
          }
        ],
        "existing_mrn": "MRN789456"
      }
    }
    ```

    **Data Fields:**

    | Field            | Type   | Description                                            |
    | ---------------- | ------ | ------------------------------------------------------ |
    | `patient_id`     | string | Cobalt's identifier for the failed patient creation    |
    | `failure_reason` | string | Legacy human-readable error message                    |
    | `reasons`        | array  | Structured error objects with `code` and `description` |
    | `existing_mrn`   | string | MRN of existing patient (for duplicate errors)         |

    **Common Error Codes:**

    * `DUPLICATE_PATIENT` — Patient already exists with same name/DOB
    * `INVALID_DATE_OF_BIRTH` — Date of birth format is invalid
    * `REQUIRED_FIELD_MISSING` — Required demographic field missing
    * `INSURANCE_NOT_FOUND` — Insurance company not found in EHR
    * `INVALID_INSURANCE_POLICY_TYPE` — Insurance policy type invalid
    * `PCP_PROVIDER_NOT_FOUND` — Primary care provider not found
    * `REFERRING_PROVIDER_NOT_FOUND` — Referring provider not found
    * `PHARMACY_NOT_FOUND` — Pharmacy not found in EHR
    * `PERMISSION_DENIED` — User lacks permission to create patients
    * `TERMINAL_FAILURE` — Unspecified terminal failure
  </Accordion>

  <Accordion title="patient.updated" icon="user-pen">
    Triggered when Cobalt applies an update to an existing patient in your EHR. Contact/address fields and provider fields are applied independently, so an update can succeed in part. `failed_fields` and `message` are present **only** when some requested fields could not be applied; a fully successful update omits them.

    **Payload Example (partial update):**

    ```json theme={null}
    {
      "id": "550e8400-e29b-41d4-a716-446655440011",
      "access_token_reference_id": "clinic_1",
      "object": "event",
      "created": "2025-10-21T14:30:00.000Z",
      "type": "patient.updated",
      "job_id": "12345",
      "data": {
        "patient_id": "xyz789uvw012",
        "mrn": "MRN123456",
        "updated_fields": ["cell_phone"],
        "failed_fields": [
          {
            "field": "referred_to_provider_id",
            "reason": "eClinicalWorks blocked this change (HTTP 403). This is typically a Workers' Comp or not-fully-registered patient, for which provider and billing fields cannot be modified via the API until the record is fully registered in the EHR."
          }
        ],
        "message": "Updated cell_phone. Could not update referred_to_provider_id (...)."
      }
    }
    ```

    **Data Fields:**

    | Field            | Type   | Description                                                                                                                   |
    | ---------------- | ------ | ----------------------------------------------------------------------------------------------------------------------------- |
    | `patient_id`     | string | Cobalt's unique identifier for the patient                                                                                    |
    | `mrn`            | string | Patient's Medical Record Number                                                                                               |
    | `updated_fields` | array  | Fields that were successfully applied to the EHR                                                                              |
    | `failed_fields`  | array  | Present only on a partial update. Objects with `field` and a human-readable `reason` for each field that could not be applied |
    | `message`        | string | Present only on a partial update. Human-readable summary of what was and was not applied                                      |
  </Accordion>

  <Accordion title="patient.update_failed" icon="user-xmark">
    Triggered when Cobalt could not apply any of the requested fields to an existing patient (for example, the EHR rejected every change). When at least one field is applied, a `patient.updated` event carrying `failed_fields` is sent instead.

    **Payload Example:**

    ```json theme={null}
    {
      "id": "550e8400-e29b-41d4-a716-446655440012",
      "access_token_reference_id": "clinic_1",
      "object": "event",
      "created": "2025-10-21T14:30:00.000Z",
      "type": "patient.update_failed",
      "job_id": "12345",
      "data": {
        "patient_id": "xyz789uvw012",
        "mrn": "MRN123456",
        "message": "eClinicalWorks blocked this change (HTTP 403). This is typically a Workers' Comp or not-fully-registered patient, for which provider and billing fields cannot be modified via the API until the record is fully registered in the EHR.",
        "sync_parameters": {
          "referred_to_provider_id": "17832387"
        }
      }
    }
    ```

    **Data Fields:**

    | Field             | Type   | Description                                           |
    | ----------------- | ------ | ----------------------------------------------------- |
    | `patient_id`      | string | Cobalt's identifier for the patient                   |
    | `mrn`             | string | Patient's Medical Record Number                       |
    | `message`         | string | Human-readable reason the update could not be applied |
    | `sync_parameters` | object | The update fields that were attempted                 |
  </Accordion>

  <Accordion title="patient.insurance.added" icon="id-card">
    Triggered when Cobalt successfully adds insurance information for a patient.

    **Payload Example:**

    ```json theme={null}
    {
      "id": "evt_abc123def456",
      "access_token_reference_id": "clinic_1",
      "object": "event",
      "created": "2025-01-15T10:30:00.000Z",
      "type": "patient.insurance.added",
      "job_id": "12345",
      "data": {
        "patient_insurance_id": "xyz789abc123",
        "patient_mrn": "MRN123456",
        "member_id": "MEM123456789",
        "group_id": "GRP98999",
        "plan_begin_date": "2025-01-01",
        "copayment": 25,
        "priority": 1,
        "relationship_to_subscriber": "self",
        "emr_insurance_id": "51638"
      }
    }
    ```

    **Data Fields:**

    | Field                        | Type           | Description                                         |
    | ---------------------------- | -------------- | --------------------------------------------------- |
    | `patient_insurance_id`       | string         | Cobalt's unique identifier for the insurance record |
    | `patient_mrn`                | string         | Patient's Medical Record Number                     |
    | `member_id`                  | string         | Insurance member ID                                 |
    | `group_id`                   | string         | Insurance group ID                                  |
    | `plan_begin_date`            | string         | Insurance plan start date (YYYY-MM-DD)              |
    | `copayment`                  | number         | Copayment amount                                    |
    | `priority`                   | number         | Insurance priority / sequence                       |
    | `relationship_to_subscriber` | string \| null | Relationship to the subscriber                      |
    | `emr_insurance_id`           | string \| null | EMR-native insurance record ID                      |

    `audit_trail` may also be present for integrations such as `Experity` and `EZDERM`. It is not currently included in the `eClinicalWorks` insurance success payload shown above.
  </Accordion>

  <Accordion title="patient.insurance.updated" icon="id-card-clip">
    Triggered when Cobalt successfully updates an existing insurance record for a patient.

    **Payload Example:**

    ```json theme={null}
    {
      "id": "evt_abc123def456",
      "access_token_reference_id": "clinic_1",
      "object": "event",
      "created": "2025-01-15T10:30:00.000Z",
      "type": "patient.insurance.updated",
      "job_id": "12345",
      "data": {
        "patient_insurance_id": "xyz789abc123",
        "patient_mrn": "MRN123456",
        "member_id": "MEM123456789",
        "group_id": "GRP98999",
        "plan_begin_date": "2025-01-01",
        "copayment": 25,
        "priority": 1,
        "relationship_to_subscriber": "spouse",
        "emr_insurance_id": "51638"
      }
    }
    ```

    **Data Fields:**

    | Field                        | Type           | Description                                                |
    | ---------------------------- | -------------- | ---------------------------------------------------------- |
    | `patient_insurance_id`       | string         | Cobalt's unique identifier for the insurance update record |
    | `patient_mrn`                | string         | Patient's Medical Record Number                            |
    | `member_id`                  | string         | Effective insurance member ID after the update             |
    | `group_id`                   | string         | Effective insurance group ID after the update              |
    | `plan_begin_date`            | string         | Effective insurance plan start date (YYYY-MM-DD)           |
    | `copayment`                  | number         | Effective copayment amount                                 |
    | `priority`                   | number         | Insurance priority / sequence                              |
    | `relationship_to_subscriber` | string \| null | Effective relationship to the subscriber                   |
    | `emr_insurance_id`           | string \| null | EMR-native insurance record ID                             |
  </Accordion>

  <Accordion title="patient.insurance.failed" icon="id-card-clip">
    Triggered when Cobalt fails to add or update insurance information for a patient.

    **Payload Example:**

    ```json theme={null}
    {
      "id": "evt_xyz789abc123",
      "access_token_reference_id": "clinic_1",
      "object": "event",
      "created": "2025-01-15T10:30:00.000Z",
      "type": "patient.insurance.failed",
      "job_id": "12345",
      "data": {
        "patient_insurance_id": "xyz789abc123",
        "patient_mrn": "MRN123456",
        "failure_reason": "Patient not found in EMR system"
      }
    }
    ```

    **Data Fields:**

    | Field                  | Type   | Description                                                          |
    | ---------------------- | ------ | -------------------------------------------------------------------- |
    | `patient_insurance_id` | string | Cobalt's identifier for the insurance record                         |
    | `patient_mrn`          | string | Patient's Medical Record Number                                      |
    | `failure_reason`       | string | Human-readable explanation of why the insurance add or update failed |
  </Accordion>
</AccordionGroup>

## Task Events

<AccordionGroup>
  <Accordion title="task.created" icon="list-check">
    Triggered when Cobalt successfully creates a task in your EHR system.

    **Payload Example:**

    ```json theme={null}
    {
      "id": "550e8400-e29b-41d4-a716-446655440011",
      "access_token_reference_id": "clinic_1",
      "object": "event",
      "created": "2025-10-21T14:30:00.000Z",
      "type": "task.created",
      "job_id": "12345",
      "data": {
        "task_id": "task123def456",
        "emr_task_id": "EMR-TASK-789",
        "subject": "Follow up with patient",
        "description": "Call patient regarding lab results",
        "assignee_user_id": "user-456",
        "patient_mrn": "MRN123456"
      }
    }
    ```

    **Data Fields:**

    | Field              | Type   | Description                                     |
    | ------------------ | ------ | ----------------------------------------------- |
    | `task_id`          | string | Cobalt's unique identifier for the task         |
    | `emr_task_id`      | string | The EHR's native task ID                        |
    | `subject`          | string | Task subject/title                              |
    | `description`      | string | Detailed task description                       |
    | `assignee_user_id` | string | ID of the user assigned to the task             |
    | `patient_mrn`      | string | Patient's Medical Record Number (if applicable) |
  </Accordion>

  <Accordion title="task.failed" icon="list-check">
    Triggered when Cobalt fails to create a task in your EHR system.

    **Payload Example:**

    ```json theme={null}
    {
      "id": "550e8400-e29b-41d4-a716-446655440012",
      "access_token_reference_id": "clinic_1",
      "object": "event",
      "created": "2025-10-21T14:30:00.000Z",
      "type": "task.failed",
      "job_id": "12345",
      "data": {
        "task_id": "task123def456",
        "failure_reason": "Patient MRN 'MRN123456' not found.",
        "reasons": [
          {
            "code": "PATIENT_NOT_FOUND",
            "description": "Patient MRN 'MRN123456' not found. Please update the patient MRN using the PATCH /v1/tasks/:id endpoint."
          }
        ]
      }
    }
    ```

    **Data Fields:**

    | Field            | Type   | Description                                            |
    | ---------------- | ------ | ------------------------------------------------------ |
    | `task_id`        | string | Cobalt's identifier for the failed task                |
    | `failure_reason` | string | Legacy human-readable error message                    |
    | `reasons`        | array  | Structured error objects with `code` and `description` |

    **Common Error Codes:**

    * `PATIENT_NOT_FOUND` — Patient MRN not found in EHR
    * `ASSIGNEE_NOT_FOUND` — Assigned user not found in EHR
    * `INVALID_TASK_DATA` — Task data validation failed
    * `PERMISSION_DENIED` — User lacks permission to create tasks
    * `TERMINAL_FAILURE` — Unspecified terminal failure
  </Accordion>
</AccordionGroup>

## Note Events

<AccordionGroup>
  <Accordion title="note.uploaded" icon="file-medical">
    Triggered when Cobalt successfully uploads a clinical note to your EHR system.

    The `status` field distinguishes a clean upload from a **partial success**, where the note body was written but one or more diagnosis (ICD) or procedure (CPT) codes could not be matched in the EHR. When codes are present, they are reported under `icd_codes` and `cpt_codes`, each split into `matched` and `unmatched` (and, for ICD, `skipped`).

    **Payload Example:**

    ```json theme={null}
    {
      "id": "550e8400-e29b-41d4-a716-446655440013",
      "access_token_reference_id": "clinic_1",
      "object": "event",
      "created": "2025-10-21T14:30:00.000Z",
      "type": "note.uploaded",
      "job_id": "12345",
      "data": {
        "appointment_id": "abc123def456",
        "mrn": "MRN123456",
        "timezone": "America/New_York",
        "status": "partial_success",
        "icd_codes": {
          "matched": [
            { "code": "E11.9", "name": "Type 2 diabetes mellitus without complications" }
          ],
          "unmatched": [
            { "code": "Z00.00", "reason": "Code not found in EHR system" }
          ]
        },
        "cpt_codes": {
          "matched": [
            { "code": "99213", "name": "Office/outpatient visit, established patient" }
          ],
          "unmatched": []
        }
      }
    }
    ```

    **Data Fields:**

    | Field            | Type   | Description                                                                                                         |
    | ---------------- | ------ | ------------------------------------------------------------------------------------------------------------------- |
    | `appointment_id` | string | Cobalt's appointment identifier                                                                                     |
    | `mrn`            | string | Patient's Medical Record Number                                                                                     |
    | `timezone`       | string | Timezone for the appointment                                                                                        |
    | `status`         | string | `succeeded` (all codes matched, or none provided) or `partial_success` (note uploaded but some codes did not match) |
    | `icd_codes`      | object | Present when ICD codes were provided. Contains `matched`, `unmatched`, and optionally `skipped` arrays              |
    | `cpt_codes`      | object | Present when CPT codes were provided. Contains `matched` and `unmatched` arrays                                     |

    Because the note body is written even when a code fails to match, unmatched codes are reported on `note.uploaded` (with `status: "partial_success"`) rather than `note.failed`. Check `status` and the `unmatched` arrays if you need to reconcile coding.
  </Accordion>

  <Accordion title="note.failed" icon="file-circle-xmark">
    Triggered when Cobalt fails to upload a clinical note to your EHR system. This fires only after retries are exhausted.

    **Payload Example:**

    ```json theme={null}
    {
      "id": "550e8400-e29b-41d4-a716-446655440003",
      "access_token_reference_id": "clinic_1",
      "object": "event",
      "created": "2025-10-21T14:30:00.000Z",
      "type": "note.failed",
      "job_id": "12345",
      "data": {
        "appointment_id": "abc123def456",
        "mrn": "MRN123456",
        "status": "failed",
        "failure_reason": "Text contains \"]]>\" which cannot be used in CDATA blocks. Please remove this sequence from the note text.",
        "reasons": [
          {
            "code": "UNSUPPORTED_CHARACTERS",
            "description": "Text contains \"]]>\" which cannot be used in CDATA blocks. Please remove this sequence from the note text."
          }
        ]
      }
    }
    ```

    **Data Fields:**

    | Field            | Type   | Description                                                                                      |
    | ---------------- | ------ | ------------------------------------------------------------------------------------------------ |
    | `appointment_id` | string | Cobalt's appointment identifier                                                                  |
    | `mrn`            | string | Patient's Medical Record Number                                                                  |
    | `status`         | string | Always `failed`                                                                                  |
    | `failure_reason` | string | Human-readable error message (retained for backward compatibility)                               |
    | `reasons`        | array  | Structured error objects, each with a machine-readable `code` and a human-readable `description` |

    **Error Codes (eClinicalWorks):**

    * `UNSUPPORTED_CHARACTERS` — The note text contains characters the EHR cannot accept
    * `MAX_ATTEMPTS_EXCEEDED` — Note creation failed after all retry attempts
    * `UNKNOWN_ERROR` — An unexpected error occurred
  </Accordion>
</AccordionGroup>

## Document Events

<AccordionGroup>
  <Accordion title="document.uploaded" icon="file-arrow-up">
    Triggered when Cobalt successfully uploads a document to your EHR system.

    **Payload Example:**

    ```json theme={null}
    {
      "id": "550e8400-e29b-41d4-a716-446655440004",
      "access_token_reference_id": "clinic_1",
      "object": "event",
      "created": "2025-10-21T14:30:00.000Z",
      "type": "document.uploaded",
      "data": {
        "document_id": "doc123456",
        "patient_mrn": "MRN123456",
        "folder_name": "Lab Results"
      }
    }
    ```

    **Data Fields:**

    | Field         | Type   | Description                                     |
    | ------------- | ------ | ----------------------------------------------- |
    | `document_id` | string | Cobalt's unique identifier for the document     |
    | `patient_mrn` | string | Patient's Medical Record Number                 |
    | `folder_name` | string | EHR folder/category where document was uploaded |
  </Accordion>

  <Accordion title="document.failed" icon="file-excel">
    Triggered when Cobalt fails to upload a document to your EHR system.

    **Payload Example:**

    ```json theme={null}
    {
      "id": "550e8400-e29b-41d4-a716-446655440005",
      "access_token_reference_id": "clinic_1",
      "object": "event",
      "created": "2025-10-21T14:30:00.000Z",
      "type": "document.failed",
      "data": {
        "document_id": "doc123456",
        "patient_mrn": "MRN123456",
        "folder_name": "Lab Results",
        "reasons": [
          {
            "code": "PATIENT_NOT_FOUND",
            "description": "Failed to find a patient with the given mrn."
          }
        ]
      }
    }
    ```

    **Data Fields:**

    | Field         | Type   | Description                                         |
    | ------------- | ------ | --------------------------------------------------- |
    | `document_id` | string | Cobalt's identifier for the document                |
    | `patient_mrn` | string | Patient's Medical Record Number                     |
    | `folder_name` | string | Target EHR folder/category                          |
    | `reasons`     | array  | List of error objects with `code` and `description` |

    **Common Error Codes:**

    * `PATIENT_NOT_FOUND` — Patient not found in EHR
    * `DOCUMENT_CLASS_NOT_FOUND` — Document folder/category not found
    * `INVALID_DOCUMENT_CONTENT` — Document content is invalid
    * `DOCUMENT_UPLOAD_FAILED` — Upload failed for unspecified reason
    * `UNKNOWN_ERROR` — Unexpected error occurred
  </Accordion>

  <Accordion title="document.upload_failed" icon="file-excel">
    Triggered when Cobalt fails to upload a document to eClinicalWorks specifically.

    **Payload Example:**

    ```json theme={null}
    {
      "id": "550e8400-e29b-41d4-a716-446655440006",
      "access_token_reference_id": "clinic_1",
      "object": "event",
      "created": "2025-10-21T14:30:00.000Z",
      "type": "document.upload_failed",
      "data": {
        "document_id": "doc123456",
        "patient_mrn": "MRN123456",
        "folder_name": "Lab Results",
        "failure_reason": "Document class 'Lab Results' not found in eClinicalWorks. Please verify the folder name.",
        "reasons": [
          {
            "code": "DOCUMENT_CLASS_NOT_FOUND",
            "description": "Document class 'Lab Results' not found in eClinicalWorks. Please verify the folder name."
          }
        ]
      }
    }
    ```

    **Data Fields:**

    | Field            | Type   | Description                                            |
    | ---------------- | ------ | ------------------------------------------------------ |
    | `document_id`    | string | Cobalt's identifier for the document                   |
    | `patient_mrn`    | string | Patient's Medical Record Number                        |
    | `folder_name`    | string | Target EHR folder/category                             |
    | `failure_reason` | string | Legacy human-readable error message                    |
    | `reasons`        | array  | Structured error objects with `code` and `description` |

    **Common Error Codes:**

    * `PATIENT_NOT_FOUND` — Patient not found in EHR
    * `DOCUMENT_CLASS_NOT_FOUND` — Document folder/category not found
    * `UNKNOWN_ERROR` — Unexpected error occurred during upload
  </Accordion>
</AccordionGroup>

## Message Events

<AccordionGroup>
  <Accordion title="message.created" icon="message">
    Triggered when Cobalt successfully creates a message in your EHR system.

    **Payload Example:**

    ```json theme={null}
    {
      "id": "550e8400-e29b-41d4-a716-446655440007",
      "access_token_reference_id": "clinic_1",
      "object": "event",
      "created": "2025-10-21T14:30:00.000Z",
      "type": "message.created",
      "job_id": "12345",
      "data": {
        "message_id": "msg123def456",
        "provider_id": "provider-789",
        "subject": "Patient Follow-up"
      }
    }
    ```

    **Data Fields:**

    | Field         | Type   | Description                                 |
    | ------------- | ------ | ------------------------------------------- |
    | `message_id`  | string | Cobalt's unique identifier for the message  |
    | `provider_id` | string | ID of the provider who received the message |
    | `subject`     | string | Message subject line                        |
  </Accordion>

  <Accordion title="message.failed" icon="message-xmark">
    Triggered when Cobalt fails to create a message in your EHR system.

    **Payload Example:**

    ```json theme={null}
    {
      "id": "550e8400-e29b-41d4-a716-446655440008",
      "access_token_reference_id": "clinic_1",
      "object": "event",
      "created": "2025-10-21T14:30:00.000Z",
      "type": "message.failed",
      "job_id": "12345",
      "data": {
        "message_id": "msg123def456",
        "provider_id": "provider-789",
        "failure_reason": "Failed to send message. Please try again or contact support if the issue persists.",
        "reasons": [
          {
            "code": "MESSAGE_SEND_FAILED",
            "description": "Failed to send message. Please try again or contact support if the issue persists."
          }
        ]
      }
    }
    ```

    **Data Fields:**

    | Field            | Type   | Description                                            |
    | ---------------- | ------ | ------------------------------------------------------ |
    | `message_id`     | string | Cobalt's identifier for the message                    |
    | `provider_id`    | string | ID of the target provider                              |
    | `failure_reason` | string | Legacy human-readable error message                    |
    | `reasons`        | array  | Structured error objects with `code` and `description` |

    **Common Error Codes:**

    * `MESSAGE_SEND_FAILED` — Message failed to send
    * `PROVIDER_NOT_FOUND` — Provider not found in EHR
    * `INVALID_MESSAGE_CONTENT` — Message content validation failed
    * `MESSAGE_TOO_LONG` — Message exceeds maximum length
    * `TERMINAL_FAILURE` — Unspecified terminal failure
  </Accordion>
</AccordionGroup>

## Telephone Encounter Events

<AccordionGroup>
  <Accordion title="telephone_encounter.created" icon="phone">
    Triggered when Cobalt successfully creates a telephone encounter in your EHR system.

    **Payload Example:**

    ```json theme={null}
    {
      "id": "550e8400-e29b-41d4-a716-446655440020",
      "access_token_reference_id": "clinic_1",
      "object": "event",
      "created": "2025-10-21T14:30:00.000Z",
      "type": "telephone_encounter.created",
      "job_id": "12345",
      "data": {
        "telephone_encounter_id": "abc123def456",
        "emr_encounter_id": "EMR-ENC-789",
        "patient_mrn": "MRN123456",
        "provider_id": "provider-456",
        "location_id": "location-789",
        "assigned_to_id": "user-101",
        "reason": "Medication refill request",
        "refill_medication_name": "Lisinopril 10mg"
      }
    }
    ```

    **Data Fields:**

    | Field                    | Type           | Description                                                                       |
    | ------------------------ | -------------- | --------------------------------------------------------------------------------- |
    | `telephone_encounter_id` | string         | Cobalt's unique identifier for the telephone encounter                            |
    | `emr_encounter_id`       | string         | The EHR's native encounter ID                                                     |
    | `patient_mrn`            | string         | Patient's Medical Record Number                                                   |
    | `provider_id`            | string         | EHR provider ID                                                                   |
    | `location_id`            | string         | EHR location ID                                                                   |
    | `assigned_to_id`         | string         | EHR user ID the encounter is assigned to                                          |
    | `reason`                 | string \| null | Reason for the telephone encounter                                                |
    | `refill_medication_name` | string         | Medication name (only present when a refill was requested and added successfully) |
  </Accordion>

  <Accordion title="telephone_encounter.created_partial" icon="phone-volume">
    Triggered when Cobalt creates a telephone encounter successfully but a requested medication refill could not be added.

    **Payload Example:**

    ```json theme={null}
    {
      "id": "550e8400-e29b-41d4-a716-446655440021",
      "access_token_reference_id": "clinic_1",
      "object": "event",
      "created": "2025-10-21T14:30:00.000Z",
      "type": "telephone_encounter.created_partial",
      "job_id": "12345",
      "data": {
        "telephone_encounter_id": "abc123def456",
        "emr_encounter_id": "EMR-ENC-789",
        "patient_mrn": "MRN123456",
        "provider_id": "provider-456",
        "location_id": "location-789",
        "assigned_to_id": "user-101",
        "reason": "Medication refill request",
        "refill_medication_name": "Lisinopril 10mg",
        "refill_failure_reason": "Medication not found in patient's active medication list"
      }
    }
    ```

    **Data Fields:**

    | Field                    | Type           | Description                                            |
    | ------------------------ | -------------- | ------------------------------------------------------ |
    | `telephone_encounter_id` | string         | Cobalt's unique identifier for the telephone encounter |
    | `emr_encounter_id`       | string         | The EHR's native encounter ID                          |
    | `patient_mrn`            | string         | Patient's Medical Record Number                        |
    | `provider_id`            | string         | EHR provider ID                                        |
    | `location_id`            | string         | EHR location ID                                        |
    | `assigned_to_id`         | string         | EHR user ID the encounter is assigned to               |
    | `reason`                 | string \| null | Reason for the telephone encounter                     |
    | `refill_medication_name` | string         | Requested medication name                              |
    | `refill_failure_reason`  | string         | Why the medication refill could not be added           |

    <Note>
      The encounter itself was created successfully — only the medication refill portion failed. Follow up manually to add the refill or notify the requester.
    </Note>
  </Accordion>

  <Accordion title="telephone_encounter.failed" icon="phone-xmark">
    Triggered when Cobalt fails to create a telephone encounter in your EHR system.

    **Payload Example:**

    ```json theme={null}
    {
      "id": "550e8400-e29b-41d4-a716-446655440022",
      "access_token_reference_id": "clinic_1",
      "object": "event",
      "created": "2025-10-21T14:30:00.000Z",
      "type": "telephone_encounter.failed",
      "job_id": "12345",
      "data": {
        "telephone_encounter_id": "abc123def456",
        "patient_mrn": "MRN123456",
        "refill_medication_name": "Lisinopril 10mg",
        "failure_reason": "Unable to create telephone encounter: Provider license has expired in eCW. Please update the provider license and try again."
      }
    }
    ```

    **Data Fields:**

    | Field                    | Type           | Description                                                         |
    | ------------------------ | -------------- | ------------------------------------------------------------------- |
    | `telephone_encounter_id` | string         | Cobalt's identifier for the failed telephone encounter              |
    | `patient_mrn`            | string \| null | Patient's Medical Record Number                                     |
    | `refill_medication_name` | string \| null | Requested medication name (if a refill was requested)               |
    | `failure_reason`         | string         | Detailed explanation of why the telephone encounter creation failed |
  </Accordion>

  <Accordion title="telephone_encounter_action.created" icon="phone-arrow-up-right">
    Triggered when Cobalt successfully adds an action (note/update) to an existing telephone encounter.

    **Payload Example:**

    ```json theme={null}
    {
      "id": "550e8400-e29b-41d4-a716-446655440023",
      "access_token_reference_id": "clinic_1",
      "object": "event",
      "created": "2025-10-21T14:30:00.000Z",
      "type": "telephone_encounter_action.created",
      "job_id": "12345",
      "data": {
        "action_id": "act123def456",
        "emr_appointment_id": "EMR-ENC-789",
        "text": "Spoke with patient — refill approved by Dr. Smith. Patient instructed to pick up at preferred pharmacy."
      }
    }
    ```

    **Data Fields:**

    | Field                | Type   | Description                                                |
    | -------------------- | ------ | ---------------------------------------------------------- |
    | `action_id`          | string | Cobalt's unique identifier for the action                  |
    | `emr_appointment_id` | string | The EHR's encounter/appointment ID the action was added to |
    | `text`               | string | The action text/note that was added                        |
  </Accordion>

  <Accordion title="telephone_encounter_action.failed" icon="phone-xmark">
    Triggered when Cobalt fails to add an action to a telephone encounter.

    **Payload Example:**

    ```json theme={null}
    {
      "id": "550e8400-e29b-41d4-a716-446655440024",
      "access_token_reference_id": "clinic_1",
      "object": "event",
      "created": "2025-10-21T14:30:00.000Z",
      "type": "telephone_encounter_action.failed",
      "job_id": "12345",
      "data": {
        "action_id": "act123def456",
        "emr_appointment_id": "EMR-ENC-789",
        "failure_reason": "ECW API returned non-success status: failure. Error: Encounter is locked"
      }
    }
    ```

    **Data Fields:**

    | Field                | Type           | Description                                            |
    | -------------------- | -------------- | ------------------------------------------------------ |
    | `action_id`          | string         | Cobalt's identifier for the action                     |
    | `emr_appointment_id` | string \| null | The EHR's encounter/appointment ID the action targeted |
    | `failure_reason`     | string         | Detailed explanation of why the action failed          |
  </Accordion>
</AccordionGroup>

## Test Event

<AccordionGroup>
  <Accordion title="test.event" icon="flask">
    A test event used to verify webhook delivery and integration setup. Trigger it manually using the [Send Test Webhook](/api-reference/webhooks/send-test-webhook) endpoint (`POST /v1/webhook/test`).

    **Payload Example:**

    ```json theme={null}
    {
      "id": "test-event-1703172600000",
      "access_token_reference_id": "your-reference-id",
      "object": "event",
      "created": "2025-10-21T14:30:00.000Z",
      "type": "test.event",
      "data": {
        "message": "This is a test webhook event",
        "timestamp": "2025-10-21T14:30:00.000Z"
      }
    }
    ```

    **Data Fields:**

    | Field       | Type   | Description                       |
    | ----------- | ------ | --------------------------------- |
    | `message`   | string | Test message content              |
    | `timestamp` | string | When the test event was generated |

    **Triggering a Test Event:**

    ```bash theme={null}
    curl -X POST https://api.cobalt.run/v1/webhook/test \
      -H "client_id: YOUR_CLIENT_ID" \
      -H "client_secret: YOUR_CLIENT_SECRET" \
      -H "access_token: YOUR_ACCESS_TOKEN"
    ```

    The test event is sent to all configured webhook URLs for your client. Use it to verify your endpoint is receiving events, your signature verification logic works, and your event processing pipeline is healthy.

    <Note>
      You must have at least one webhook URL configured (`POST /v1/webhook`) before sending a test event.
    </Note>
  </Accordion>
</AccordionGroup>
