# Activate Account Source: https://docs.usecobalt.com/api-reference/account/activate POST /account/activate Activates a deactivated account. ### Example Request ```bash theme={null} curl -X POST https://api.usecobalt.com/v1/account/activate \ -H 'Content-Type: application/json' \ -H 'client_id: ci_live_198908HJDKJSH98789OHKJL' \ -H 'client_secret: cs_live_9827hofdsklOYYHJLJh' \ -H 'access_token: 493JKLHIU98789hLKH9HHJH' ``` ### Example Response ```json theme={null} { "success": true, "message": "Account activated successfully" } ``` # Deactivate Account Source: https://docs.usecobalt.com/api-reference/account/deactivate POST /account/deactivate Deactivates an active account. ### Example Request ```bash theme={null} curl -X POST https://api.usecobalt.com/v1/account/deactivate \ -H 'Content-Type: application/json' \ -H 'client_id: ci_live_198908HJDKJSH98789OHKJL' \ -H 'client_secret: cs_live_9827hofdsklOYYHJLJh' \ -H 'access_token: 493JKLHIU98789hLKH9HHJH' ``` ### Example Response ```json theme={null} { "success": true, "message": "Account deactivated successfully" } ``` # Delete Credential Source: https://docs.usecobalt.com/api-reference/account/delete-credential DELETE /credentials/{credentialId} Removes a credential set from a connection. Deletes a set of EHR credentials. Unlike [Disable Credential](/api-reference/account/disable-credential), this cannot be undone — the credential set cannot be re-enabled afterwards. `credentialId` is the UUID of the credential set. Account events carry it as `credential_id`, so you can delete the exact credential an event reports on — see [Account Events](/docs/webhooks/account-events). A connection must always keep one active credential set, so deleting the last one returns `409`. ### Example Request ```bash theme={null} curl -X DELETE https://api.usecobalt.com/v1/credentials/550e8400-e29b-41d4-a716-446655440010 \ -H 'Content-Type: application/json' \ -H 'client_id: ci_live_198908HJDKJSH98789OHKJL' \ -H 'client_secret: cs_live_9827hofdsklOYYHJLJh' \ -H 'access_token: 493JKLHIU98789hLKH9HHJH' ``` ### Example Response ```json theme={null} { "success": true, "message": "Credential deleted" } ``` ### Last Active Credential ```json theme={null} { "success": false, "message": "Cannot delete the last active credential set. Add or enable another credential first." } ``` # Disable Credential Source: https://docs.usecobalt.com/api-reference/account/disable-credential PATCH /credentials/{credentialId}/disable Stops Cobalt using a credential set for EHR sessions, leaving it in place. A connection can hold several sets of EHR credentials. Disabling one takes it out of rotation without removing it, so you can re-enable it later. Sessions already running on that credential are not interrupted. `credentialId` is the UUID of the credential set. Account events carry it as `credential_id`, so you can disable the exact credential an event reports on — see [Account Events](/docs/webhooks/account-events). A connection must always keep one active credential set, so disabling the last one returns `409`. ### Example Request ```bash theme={null} curl -X PATCH https://api.usecobalt.com/v1/credentials/550e8400-e29b-41d4-a716-446655440010/disable \ -H 'Content-Type: application/json' \ -H 'client_id: ci_live_198908HJDKJSH98789OHKJL' \ -H 'client_secret: cs_live_9827hofdsklOYYHJLJh' \ -H 'access_token: 493JKLHIU98789hLKH9HHJH' ``` ### Example Response ```json theme={null} { "success": true, "message": "Credential disabled" } ``` ### Last Active Credential ```json theme={null} { "success": false, "message": "Cannot disable the last active credential set. Add or enable another credential first." } ``` # Enable Credential Source: https://docs.usecobalt.com/api-reference/account/enable-credential PATCH /credentials/{credentialId}/enable Puts a disabled credential set back into rotation. Re-enables a credential set that was previously disabled. A deleted credential set cannot be restored this way. `credentialId` is the UUID of the credential set, the same value account events carry as `credential_id`. ### Example Request ```bash theme={null} curl -X PATCH https://api.usecobalt.com/v1/credentials/550e8400-e29b-41d4-a716-446655440010/enable \ -H 'Content-Type: application/json' \ -H 'client_id: ci_live_198908HJDKJSH98789OHKJL' \ -H 'client_secret: cs_live_9827hofdsklOYYHJLJh' \ -H 'access_token: 493JKLHIU98789hLKH9HHJH' ``` ### Example Response ```json theme={null} { "success": true, "message": "Credential enabled" } ``` # Lock Client Source: https://docs.usecobalt.com/api-reference/account/lock POST /client/lock Emergency stop: immediately invalidates every access token, EHR session, and your client_secret. Break-glass endpoint for a broad compromise. Locking is immediate and affects all users under your client: all access tokens are deactivated, all EHR sessions are invalidated, and your `client_secret` is cleared. Only `client_id` and `client_secret` headers are required. Locking is not self-serve reversible — contact support to unlock, after which users reconnect through [Cobalt Link](/docs/link/overview). ### Example Request ```bash theme={null} curl -X POST https://api.usecobalt.com/v1/client/lock \ -H 'Content-Type: application/json' \ -H 'client_id: ci_live_198908HJDKJSH98789OHKJL' \ -H 'client_secret: cs_live_9827hofdsklOYYHJLJh' ``` ### Example Response ```json theme={null} { "success": true, "message": "Account has been locked. Contact support to unlock.", "data": { "cookies_invalidated": 12, "tokens_deactivated": 12, "users_deactivated": 12, "locked_at": "2026-07-20T19:45:00.000Z" } } ``` # Rotate Access Token Source: https://docs.usecobalt.com/api-reference/account/rotate-access-token POST /access-token/rotate Issues a new access token and immediately invalidates the current one. Use this when an access token may have been exposed. The user's EHR connection is unaffected and no re-link is required — store the returned token and continue. Requests made with the old token return `401`. ### Example Request ```bash theme={null} curl -X POST https://api.usecobalt.com/v1/access-token/rotate \ -H 'Content-Type: application/json' \ -H 'client_id: ci_live_198908HJDKJSH98789OHKJL' \ -H 'client_secret: cs_live_9827hofdsklOYYHJLJh' \ -H 'access_token: 493JKLHIU98789hLKH9HHJH' ``` ### Example Response ```json theme={null} { "success": true, "message": "Access token rotated successfully. The old token has been invalidated immediately.", "access_token": "8f2b41c09e77d3a5f6b1a0c4e9d82f7a" } ``` # Create Appointments Source: https://docs.usecobalt.com/api-reference/appointments/create POST /appointments Creates a new appointment for a patient. ### Provider and Location IDs When creating appointments, use the **`ehr_id`** values from your providers and locations: * **`provider`**: Use the `ehr_id` from `GET /v1/providers` (not the `id`) * **`location`**: Use the `ehr_id` from `GET /v1/locations` (not the `id`) * **`secondary_provider`**: Use the `ehr_id` from `GET /v1/providers` (not the `id`) These values represent the identifiers your EMR system uses. Cobalt passes them directly to your EMR when creating the appointment. **Don't use the Cobalt `id` here.** The `id` field from GET responses is Cobalt's internal UUID, used only for updating provider/location settings via PATCH endpoints. ### Date and Time Formats You have two options for specifying the appointment date and time: #### Option 1: ISO 8601 Format (Recommended) Use the `datetime` parameter with full ISO 8601 timestamp including timezone: ```bash theme={null} curl -X POST https://api.usecobalt.com/v1/appointments \ -H 'Content-Type: application/json' \ -H 'client_id: ci_live_198908HJDKJSH98789OHKJL' \ -H 'client_secret: cs_live_9827hofdsklOYYHJLJh' \ -H 'access_token: 493JKLHIU98789hLKH9HHJH' \ -d '{ "mrn": "123456789", "location": "123456789", "datetime": "2025-01-01T10:00:00-05:00", "provider": "123456789", "type": "new_patient" }' ``` **Format:** `YYYY-MM-DDTHH:mm:ss±HH:MM` (24-hour time with timezone offset) #### Option 2: Separate Date and Time Use `date` and `time` parameters separately. **Important:** When using this option, the `time` must be in 12-hour format with `am` or `pm`. ```bash theme={null} curl -X POST https://api.usecobalt.com/v1/appointments \ -H 'Content-Type: application/json' \ -H 'client_id: ci_live_198908HJDKJSH98789OHKJL' \ -H 'client_secret: cs_live_9827hofdsklOYYHJLJh' \ -H 'access_token: 493JKLHIU98789hLKH9HHJH' \ -d '{ "mrn": "123456789", "location": "123456789", "date": "2025-01-01", "time": "10:00 am", "provider": "123456789", "type": "new_patient" }' ``` **Date Format:** `YYYY-MM-DD` **Time Format:** `h:mm am/pm` or `hh:mm am/pm` (12-hour format, must include am/pm) Common mistake: Using `"time": "10:00"` without `am` or `pm` will result in an error. Always include `am` or `pm` when using separate date and time parameters. #### Booking Timezone By default the appointment time is interpreted in the account's timezone. For EHR instances whose facilities span multiple timezones, pass an optional `timezone` (IANA name) to book the appointment at that facility's local wall-clock time. The time you send (via `datetime` or `date` and `time`) is then treated as local to that zone. ```bash theme={null} curl -X POST https://api.usecobalt.com/v1/appointments \ -H 'Content-Type: application/json' \ -H 'client_id: ci_live_198908HJDKJSH98789OHKJL' \ -H 'client_secret: cs_live_9827hofdsklOYYHJLJh' \ -H 'access_token: 493JKLHIU98789hLKH9HHJH' \ -d '{ "mrn": "123456789", "location": "123456789", "date": "2025-01-01", "time": "9:20 am", "timezone": "America/Chicago", "provider": "123456789", "type": "new_patient" }' ``` ### Preventing Double Bookings Set `prevent_double_booking` to `"true"` to have Cobalt check the provider's schedule for conflicts before submitting the appointment to your EHR. If the requested time slot overlaps with an existing patient visit or a schedule block, the appointment fails immediately with a descriptive error rather than being submitted. ```bash theme={null} curl -X POST https://api.usecobalt.com/v1/appointments \ -H 'Content-Type: application/json' \ -H 'client_id: ci_live_198908HJDKJSH98789OHKJL' \ -H 'client_secret: cs_live_9827hofdsklOYYHJLJh' \ -H 'access_token: 493JKLHIU98789hLKH9HHJH' \ -d '{ "mrn": "123456789", "location": "123456789", "datetime": "2025-01-01T10:00:00-05:00", "provider": "123456789", "type": "follow_up", "prevent_double_booking": "true" }' ``` When a conflict is detected, you will receive an `appointment.failed` webhook with the `conflict_type` and `conflict_details` fields populated (see [Failure Examples](#failure-examples) below). If Cobalt is unable to check the provider's schedule due to a network or EHR connectivity issue, the appointment proceeds normally rather than blocking. The conflict check is a best-effort safeguard. ### Complete Example Request ```bash theme={null} curl -X POST https://api.usecobalt.com/v1/appointments \ -H 'Content-Type: application/json' \ -H 'client_id: ci_live_198908HJDKJSH98789OHKJL' \ -H 'client_secret: cs_live_9827hofdsklOYYHJLJh' \ -H 'access_token: 493JKLHIU98789hLKH9HHJH' \ -d '{ "mrn": "123456789", "location": "123456789", "date": "2025-01-01", "time": "10:00 am", "provider": "123456789", "secondary_provider": "123456789", "type": "new_patient", "note": "This is a test appointment", "reason": "Patient requested appointment for routine checkup", "department_id": "1", "non_billable": "false" }' ``` ### Example Response ```json theme={null} { "success": true, "message": "Appointment processing. A webhook event will be sent upon completion.", "appointment_id": "123456789", "job_id": 15550942 } ``` ### Webhook Notifications When the appointment processing is complete, we will send a webhook to your registered endpoint. Here are examples of what those webhook payloads will look like: #### Success ```json theme={null} { "id": "evt_1J9X2q2eZvKYlo2Cmnopqr", "access_token_reference_id": "user_1J9X2q2eZvKYlo2Cstuv", "object": "event", "created": "2023-10-28T11:00:00Z", "type": "appointment.created", "data": { "appointment_id": "123456789", "date_time": "2025-01-01T10:00", "timezone": "America/New_York", "provider_id": "emr_prov_123", "secondary_provider_id": "emr_sec_prov_456", "provider_name": "Dr. Smith", "mrn": "123456789" } } ``` #### Failure Examples The `appointment.failed` webhook event includes a `failure_reason` and can contain additional fields in the `data` object depending on the cause of the failure. **Patient Not Found:** ```json theme={null} { "id": "evt_1J9X2q2eZvKYlo2Cwxyz", "access_token_reference_id": "user_1J9X2q2eZvKYlo2Cstuv", "object": "event", "created": "2023-10-28T11:05:00Z", "type": "appointment.failed", "data": { "appointment_id": "123456790", "mrn": "000000", "failure_reason": "Patient MRN '000000' not found in eCW. Please update the MRN using the PATCH /v1/appointments/:id endpoint." } } ``` **Visit Type Not Found:** ```json theme={null} { "id": "evt_1J9X2q2eZvKYlo2Cabcde", "access_token_reference_id": "user_1J9X2q2eZvKYlo2Cstuv", "object": "event", "created": "2023-10-28T11:10:00Z", "type": "appointment.failed", "data": { "appointment_id": "123456791", "mrn": "123456789", "failure_reason": "Visit type \"Annual Wellness Visit\" not available for the selected resource/provider/location combination in eCW. Allowed visit types for this resource: New Patient, Follow Up, Telehealth. Please update the visit type using the PATCH /v1/appointments/:id endpoint, or re-create with skip_visit_type_validation: \"true\" if this visit type is bookable in eCW (e.g. via availability slots).", "visit_type": "Annual Wellness Visit", "allowed_visit_types": ["New Patient", "Follow Up", "Telehealth"] } } ``` If the visit type is bookable in your EHR but not part of the resource's configured visit type list (for example, visit types offered through availability slots), re-create the appointment with `skip_visit_type_validation` set to `"true"` and an explicit `duration`. **Provider Not Found:** ```json theme={null} { "id": "evt_1J9X2q2eZvKYlo2Cfghij", "access_token_reference_id": "user_1J9X2q2eZvKYlo2Cstuv", "object": "event", "created": "2023-10-28T11:15:00Z", "type": "appointment.failed", "data": { "appointment_id": "123456792", "mrn": "123456789", "failure_reason": "Provider 'Dr. Unknown' not found in eCW. Please check the provider configuration or use PATCH /v1/appointments/:id endpoint to update.", "provider_name": "Dr. Unknown" } } ``` **Schedule Block Conflict:** ```json theme={null} { "id": "evt_1J9X2q2eZvKYlo2Cklmno", "access_token_reference_id": "user_1J9X2q2eZvKYlo2Cstuv", "object": "event", "created": "2023-10-28T11:20:00Z", "type": "appointment.failed", "data": { "appointment_id": "123456793", "mrn": "123456789", "failure_reason": "Appointment creation failed: The appointment conflicts with a schedule block (Admin Block 9am-12pm). Permission denied. Please choose a different time or resolve the schedule conflict.", "conflict_type": "schedule_block", "block_details": "Admin Block 9am-12pm" } } ``` **Appointment Conflict (Permission Denied):** ```json theme={null} { "id": "evt_1J9X2q2eZvKYlo2Cpqrst", "access_token_reference_id": "user_1J9X2q2eZvKYlo2Cstuv", "object": "event", "created": "2023-10-28T11:25:00Z", "type": "appointment.failed", "data": { "appointment_id": "123456794", "mrn": "123456789", "failure_reason": "Appointment creation failed: This appointment conflicts with another existing appointment. Permission denied. Please choose a different time.", "conflict_type": "appointment_conflict" } } ``` **Double Booking Conflict — Existing Patient Visit:** ```json theme={null} { "id": "evt_1J9X2q2eZvKYlo2Cuvwxy", "access_token_reference_id": "user_1J9X2q2eZvKYlo2Cstuv", "object": "event", "created": "2023-10-28T11:28:00Z", "type": "appointment.failed", "data": { "appointment_id": "123456796", "patient_mrn": "123456789", "failure_reason": "Provider already has an appointment with Doe, Jane from 10:00 AM to 10:15 AM", "conflict_type": "existing_patient_visit", "conflict_details": { "patient_name": "Doe, Jane", "start_time": "10:00 AM", "end_time": "10:15 AM", "visit_type": "F/U" } } } ``` **Double Booking Conflict — Schedule Block:** ```json theme={null} { "id": "evt_1J9X2q2eZvKYlo2Cuvwxz", "access_token_reference_id": "user_1J9X2q2eZvKYlo2Cstuv", "object": "event", "created": "2023-10-28T11:29:00Z", "type": "appointment.failed", "data": { "appointment_id": "123456797", "patient_mrn": "123456789", "failure_reason": "Provider has a scheduled block (Admin Block) from 9:00 AM to 12:00 PM", "conflict_type": "scheduled_block", "conflict_details": { "block_description": "Admin Block", "start_time": "9:00 AM", "end_time": "12:00 PM" } } } ``` **Generic Failure (Max Retries):** ```json theme={null} { "id": "evt_1J9X2q2eZvKYlo2Cuvwxyz", "access_token_reference_id": "user_1J9X2q2eZvKYlo2Cstuv", "object": "event", "created": "2023-10-28T11:30:00Z", "type": "appointment.failed", "data": { "appointment_id": "123456795", "mrn": "123456789", "failure_reason": "Appointment creation failed after 3 attempts. Last error: Some internal error message. Please check logs or contact support." } } ``` # Fetch Appointments Source: https://docs.usecobalt.com/api-reference/appointments/fetch POST /appointments/fetch Performs a live fetch of appointment data from the connected EMR system by date range, patient MRN, or provider. This is a slow operation that fetches up-to-date appointment data directly from the EMR system. If the newest data is not critical, consider using the GET /appointments endpoint instead for faster results from cached data. ## Search Modes ### Date Range (default) Fetches appointments for a date range (max 7 days). Results are synced to the database and returned via webhook. ```json theme={null} { "start_date": "2025-01-01", "end_date": "2025-01-07", "include": ["ehr_audit_log"] } ``` ### MRN Fetches all appointments for a specific patient by MRN, enriched with `created_at` and `created_by` timestamps. ```json theme={null} { "search_by": "mrn", "mrn": "12345" } ``` ### Provider Fetches appointments for one or more providers in a single EMR call. Results are synced to the database (scoped to the requested providers) and returned via webhook. * `provider_ids`: required array of EMR provider IDs (max 10) * 1 provider: `end_date` may be up to 5 days after `start_date` * 2-10 providers: `start_date` must equal `end_date` ```json theme={null} { "search_by": "provider", "start_date": "2025-01-01", "end_date": "2025-01-05", "provider_ids": ["prov_789", "prov_790"], "include": ["progress_note"] } ``` ## Visit Notes Where the EMR supports it, each appointment in the webhook has a `visit_note_document_id`: the Cobalt document ID of the encounter's visit note. Pass it to [POST /documents/fetch](/api-reference/documents/fetch) to get the note as a PDF. The same encounter has the same ID in the appointment and patient live fetches. ## Webhook Notifications When the appointment fetch is complete, we will send a webhook to your registered endpoint. ### Date Range Webhook When the request includes `ehr_audit_log`, each appointment also carries `created_at`, `created_by`, and an `ehr_audit_log` array of every field change (who changed it, when, and old → new): ```json theme={null} { "id": "evt_1J9X2q2eZvKYlo2Cmnopqr", "access_token_reference_id": "user_1J9X2q2eZvKYlo2Cstuv", "job_id": "job_1J9X2q2eZvKYlo2Cmnopqr", "object": "event", "created": "2023-10-28T11:00:00Z", "timestamp": "2023-10-28T11:00:00Z", "type": "appointment.live_fetch_completed", "action": "sync", "data": { "success": true, "appointment_count": 42, "start_date": "2025-01-01", "end_date": "2025-01-07", "appointments": [ { "id": "a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4", "ehr_appointment_id": "12345678", "datetime": "2025-01-15T10:00:00-05:00", "duration": 30, "appointment_type": "Office Visit", "appointment_mode": null, "status": "CHK (Check Out)", "provider_ehr_id": "prov_789", "provider_name": "Smith, Jane", "provider_npi": "1234567890", "location": "Main Office", "patient_mrn": "85799", "patient_first_name": "Alex", "patient_last_name": "Doe", "patient_dob": "1985-04-12", "patient_gender": "M", "patient_phone": "555-555-5555", "patient_ehr_id": "PAT123", "cobalt_patient_id": "bbbbbbbbccccddddeeeeffffffffffff", "insurance": "Aetna", "insurances": null, "insurance_subscriber": null, "insurance_number": "W123456789", "insurance_group_number": null, "secondary_insurance": null, "secondary_insurance_number": null, "diagnoses": null, "created_at": "2025-01-10T09:15:22-05:00", "created_by": "Doe, John", "ehr_audit_log": [ { "field_name": "Visit Status", "old_value": "READY (Ready To Admit)", "new_value": "CHK (Check Out)", "action": "Modified", "username": "Smith, Jane", "timestamp": "2025-01-15T10:42:00-05:00" }, { "field_name": "Visit Status", "old_value": "ARR (Check-in)", "new_value": "READY (Ready To Admit)", "action": "Modified", "username": "Roe, Mary", "timestamp": "2025-01-15T10:18:33-05:00" }, { "field_name": "Visit Status", "old_value": "CON (Confirmed)", "new_value": "ARR (Check-in)", "action": "Modified", "username": "Smith, Jane", "timestamp": "2025-01-15T09:58:12-05:00" }, { "field_name": "Visit Status", "old_value": "PEN (Pending)", "new_value": "CON (Confirmed)", "action": "Modified", "username": "Doe, John", "timestamp": "2025-01-11T14:05:40-05:00" }, { "field_name": "Visit Status", "old_value": "", "new_value": "PEN (Pending)", "action": "Created", "username": "Doe, John", "timestamp": "2025-01-10T09:15:22-05:00" } ] } ] } } ``` ### MRN Webhook ```json theme={null} { "id": "evt_1J9X2q2eZvKYlo2Cmnopqr", "access_token_reference_id": "user_1J9X2q2eZvKYlo2Cstuv", "job_id": "job_1J9X2q2eZvKYlo2Cmnopqr", "object": "event", "created": "2023-10-28T11:00:00Z", "timestamp": "2023-10-28T11:00:00Z", "type": "appointment.live_fetch_completed", "action": "sync", "data": { "success": true, "search_by": "mrn", "mrn": "12345", "appointment_count": 7, "appointments": [ { "ehr_appointment_id": "12345678", "datetime": "2025-04-07T13:00:00.000-07:00", "appointment_type": "OV (Office Visit)", "status": "PEN (Pending)", "provider_ehr_id": "prov_456", "provider_first_name": "Jane", "provider_last_name": "Smith", "reason": "Annual Checkup", "location": "Main Office", "locked": false, "created_at": "2025-02-20T07:52:02.000-08:00", "created_by": "Doe, John" } ] } } ``` ### Provider Webhook Each appointment uses the same shape as the Date Range Webhook above. ```json theme={null} { "id": "evt_1J9X2q2eZvKYlo2Cmnopqr", "access_token_reference_id": "user_1J9X2q2eZvKYlo2Cstuv", "job_id": "job_1J9X2q2eZvKYlo2Cmnopqr", "object": "event", "created": "2023-10-28T11:00:00Z", "timestamp": "2023-10-28T11:00:00Z", "type": "appointment.live_fetch_completed", "action": "sync", "data": { "success": true, "search_by": "provider", "provider_ids": ["prov_789", "prov_790"], "start_date": "2025-01-01", "end_date": "2025-01-05", "appointment_count": 12, "appointments": [ { "id": "a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4", "ehr_appointment_id": "12345678", "datetime": "2025-01-02T10:00:00-05:00", "duration": 30, "appointment_type": "Office Visit", "status": "CHK (Check Out)", "provider_ehr_id": "prov_789", "provider_name": "Smith, Jane", "provider_npi": "1234567890", "location": "Main Office", "patient_mrn": "85799", "patient_first_name": "Alex", "patient_last_name": "Doe", "patient_ehr_id": "PAT123", "cobalt_patient_id": "bbbbbbbbccccddddeeeeffffffffffff", "progress_note": "Progress Notes Patient: Doe, Alex DOB: 04/12/1985 PRISMA Summary: External Timeline: The patient has had 1 ER/Hospital visit at an outside facility. External Results: The patient has undergone labs including CBC. Subjective: Chief Complaint(s): Follow up. HPI: Doing well since last visit. Assessment: Hypertension. Treatment: Continue current medication. Follow Up: 3 months" } ] } } ``` # Get Appointments Source: https://docs.usecobalt.com/api-reference/appointments/get GET /appointments Gets a list of appointments. By default only real appointments are returned. Use `category` to include schedule slots and blocks, for example `?category=appointment,schedule_slot` or `?category=schedule_slot` for open availability only. The legacy `include_schedule_blocks=true` parameter is deprecated but still honored: it is equivalent to `?category=appointment,schedule_block`. Prefer `category`, which also supports schedule slots. `category` takes precedence if both are supplied. # Get Appointment by ID Source: https://docs.usecobalt.com/api-reference/appointments/get-by-id GET /appointments/{id} Gets details for a specific appointment. ### Example Request ```bash theme={null} curl -X GET https://api.usecobalt.com/v1/appointments/1276397812 \ -H 'Content-Type: application/json' \ -H 'client_id: ci_live_198908HJDKJSH98789OHKJL' \ -H 'client_secret: cs_live_9827hofdsklOYYHJLJh' \ -H 'access_token: 493JKLHIU98789hLKH9HHJH' ``` ### Example Response ```json theme={null} { "success": true, "appointment": { "id": "1276397812", "datetime": "2024-03-14T00:00:00-07:00", "appointment_mode": "office", "appointment_type": "f/u", "location": "54252", "patient_name": "Jane Doe", "patient_mrn": "414421", "patient_dob": "1994-08-12", "patient_gender": "female", "insurance": "Blue Shield", "insurance_subscriber": "John Doe", "insurance_number": "19228934", "insurance_group_number": "FDL992829qe", "reason": "Routine follow-up", "provider_ehr_id": "6644", "provider_npi": "2453453453", "status": "completed" } } ``` # Update Appointment Source: https://docs.usecobalt.com/api-reference/appointments/update PATCH /appointments/{id} Updates an existing appointment. The update process differs based on the current status of the appointment. **Use the Cobalt `id` in the URL path.** The `{id}` parameter should be the Cobalt appointment ID returned from API responses or GET endpoints, not the EHR ID. The update process differs based on the current status of the appointment: ### 1. Updating Pending or Failed Appointments For appointments with a status of 'pending' or 'failed', you can update any attribute of the appointment. | Parameter | Type | Required | Description | | - | - | - | - | | Any appointment attribute | varies | No | Any attribute of the appointment can be updated. All fields are optional. | ### 2. Updating Scheduled Appointments For appointments with a status of 'scheduled', you can update the status, note, or visit type. **Rescheduling (changing date/time):** a Scheduled appointment's date/time cannot be edited in place. To reschedule, follow the standard flow clinics use to keep cancellations tracked: PATCH the existing appointment's `status` to a cancelled status (from `GET /visit-statuses`), then create a new appointment at the new time with `POST /v1/appointments`. | Parameter | Type | Required | Description | | - | - | - | - | | status | string | No | Must be one of the available statuses associated with the linked account. You can find the available statuses by using our GET /visit-statuses endpoint | | note | string | No | A note associated with the appointment. This is not a progress note or addition to the patient chart. This is primarily used for admin (eg. "patient might be 5 minutes late"). | | type | string | No | The new visit type for the appointment. Must be one of the visit types from GET /v1/visit-types, and must be available for the appointment's provider/resource in the EHR (skippable via `skip_visit_type_validation`). The appointment's duration is updated to the new visit type's configured duration unless `duration` is provided. | | duration | string | No | Only valid together with `type`. The new appointment duration in minutes, as a positive integer string. Overrides the new visit type's configured duration. Required when `skip_visit_type_validation` is `"true"`. | | skip\_visit\_type\_validation | string | No | Only valid together with `type`. When `"true"`, skips the check that the visit type is configured for the appointment's provider/resource in the EHR. Use this for visit types that are bookable in the EHR (for example via availability slots) but not part of the resource's configured visit type list. Requires `duration`. | ### Example Request for Updating a Pending/Failed Appointment ```bash theme={null} curl -X PATCH https://api.usecobalt.com/v1/appointments/23456789 \ -H 'Content-Type: application/json' \ -H 'client_id: ci_live_198908HJDKJSH98789OHKJL' \ -H 'client_secret: cs_live_9827hofdsklOYYHJLJh' \ -H 'access_token: 493JKLHIU98789hLKH9HHJH' \ -d '{ "appointment_datetime": "2024-01-15T14:00:00-07:00", "location": "New Clinic", "type": "f/u" }' ``` ### Example Response ```json theme={null} { "success": true, "message": "Appointment updated successfully" } ``` # Fetch Availability Source: https://docs.usecobalt.com/api-reference/availability/fetch POST /availability/fetch Performs a live fetch of provider availability by combining a real-time EMR appointment fetch with availability calculation. This is an async operation that fetches up-to-date appointment data directly from the EMR and computes availability in one pass. If freshness is not critical, use `GET /availability` instead, which returns availability against cached appointment data. ## Request Constraints * `provider_ids`: required array of EMR provider IDs. Must contain between 1 and 10 entries. * A single provider may span up to a 5-day window. * Two or more providers must share a single date (`start_date` must equal `end_date`). All availability-calculation parameters (`calculation_method`, `daily_appointment_limit`, `daily_limit_type`, `provider_type`, `fallback_to_default_hours`, `max_slot_duration`, `facility_scoped_scheduling`, `location_ids`, `cancelled_statuses`) are optional and follow the same defaults and validation rules as `GET /availability`. ### Example Request (1 provider, 5 days) ```json theme={null} { "start_date": "2026-07-01", "end_date": "2026-07-05", "provider_ids": ["prov_789"], "calculation_method": "gaps", "cancelled_statuses": ["canc", "rs", "cancsms"] } ``` ### Example Request (multiple providers, single day) ```json theme={null} { "start_date": "2026-07-01", "end_date": "2026-07-01", "provider_ids": ["prov_789", "prov_790", "prov_791"] } ``` ## Webhook Notification When the fetch completes, we send an `availability.live_fetch_completed` event to your registered webhook endpoint with the computed slots inline. The slots themselves are not persisted, but as a side effect the underlying appointments returned by the EMR are written to our appointments cache — a follow-up `GET /availability` reflects the live state. ```json theme={null} { "id": "evt_1J9X2q2eZvKYlo2Cmnopqr", "access_token_reference_id": "user_1J9X2q2eZvKYlo2Cstuv", "job_id": "job_1J9X2q2eZvKYlo2Cmnopqr", "object": "event", "created": "2026-07-01T11:00:00Z", "timestamp": "2026-07-01T11:00:00Z", "type": "availability.live_fetch_completed", "action": "sync", "data": { "success": true, "provider_ids": ["prov_789"], "start_date": "2026-07-01", "end_date": "2026-07-05", "slot_count": 24, "slots": [ { "day_of_week": 3, "date": "2026-07-01", "duration_string": "30 minutes", "range_start": "2026-07-01T09:00:00-07:00", "range_end": "2026-07-01T09:30:00-07:00", "provider_id": "prov_789", "facility_id": "loc_1", "facility_name": "Main Office" } ] } } ``` Each slot always includes `day_of_week` (1-7, ISO weekday), `date` (YYYY-MM-DD), `duration_string`, `range_start`, `range_end`, and `provider_id`. `facility_id` and `facility_name` are present when the source EHR scopes the shift to a specific location. `visit_type`, `available_appointments`, and `max_visits` appear when `calculation_method` is `slots` or `equal_slots`. # Get Availability Source: https://docs.usecobalt.com/api-reference/availability/get GET /availability Returns open scheduling slots computed from synced provider hours and booked appointments over a date range. **Provider Filtering**: Only providers with `active=true` AND `hide_in_availability=false` will appear in availability results. If you've set a provider's `hide_in_availability` to `true`, they won't appear here even if their status is active. Use `provider_ids` and `location_ids` to narrow returned availability. Both parameters accept comma-separated EHR IDs, for example `provider_ids=prov1,prov2` and `location_ids=loc1,loc2`. Location filtering is applied to generated availability windows using each slot's `facility_id`; slots without a `facility_id` are excluded when `location_ids` is provided. ### Example Request ```bash theme={null} curl -X GET https://api.usecobalt.com/v1/availability \ -H 'Content-Type: application/json' \ -H 'client_id: ci_live_198908HJDKJSH98789OHKJL' \ -H 'client_secret: cs_live_9827hofdsklOYYHJLJh' \ -H 'access_token: 493JKLHIU98789hLKH9HHJH' \ -G \ --data-urlencode "start_date=2024-03-14T00:00:00-07:00" \ --data-urlencode "end_date=2024-04-14T23:59:59-07:00" \ --data-urlencode "calculation_method=slots" \ --data-urlencode "provider_ids=3498234,9834521" \ --data-urlencode "location_ids=1" \ --data-urlencode "cancelled_statuses=canc,rs,cancsms" ``` ### Example Response ```json theme={null} { "success": true, "results": { "availability": [ { "day_of_week": 5, "date": "2024-04-05", "duration_string": "9h", "range_start": "2024-04-05T08:00:00.000-07:00", "range_end": "2024-04-05T17:00:00.000-07:00", "provider_id": "3498234", "facility_id": "1", // if available in the EHR "facility_name": "Main St Clinic", // if available in the EHR "visit_type": "New Patient" // only present when calculation_method=slots } ] }, "timezone": "America/Los_Angeles" } ``` ### Enhanced Response with Detailed Slot Information When using `calculation_method=slots`, if the EHR provides detailed slot information, the response may include additional fields for capacity management: ```json theme={null} { "success": true, "availability": [ { "day_of_week": 2, "date": "2025-01-14", "duration_string": "30 minutes", "range_start": "2025-01-14T09:00:00.000-08:00", "range_end": "2025-01-14T09:30:00.000-08:00", "provider_id": "12345", "visit_type": "New Patient Visit", "available_appointments": 3, "max_visits": 5, "facility_id": "LOC-001", "facility_name": "Main Office" }, { "day_of_week": 2, "date": "2025-01-14", "duration_string": "15 minutes", "range_start": "2025-01-14T10:00:00.000-08:00", "range_end": "2025-01-14T10:15:00.000-08:00", "provider_id": "12345", "visit_type": "Follow-up", "available_appointments": 1, "max_visits": 1, "facility_id": "LOC-001", "facility_name": "Main Office" } ], "timezone": "America/Los_Angeles" } ``` **Key fields when detailed slot information is available:** * `visit_type`: The specific appointment type that can be booked in this slot * `available_appointments`: How many more appointments can still be scheduled in this slot * `max_visits`: The total capacity of this slot (useful for understanding how many are already booked) * `facility_id`: Unique identifier for the facility/location * `facility_name`: Name of the facility/location The availability of these enhanced fields depends on your EHR system's capabilities. Not all EHRs provide this level of detail for slot-based availability. ## Calculation Methods The `calculation_method` parameter determines how availability is calculated: * **gaps** (Default): Calculates availability based on free time between a provider's working hours and existing appointments * **slots**: Pre-defined appointment slots blocked only by appointments matching the slot's visit type * **equal\_slots**: Pre-defined appointment slots blocked by ANY appointment at that time, regardless of type ### Example Scenarios **Scenario 1:** A "New Patient" schedule\_slot exists at 10:00 AM. A "Follow-up" appointment is already booked at 10:00 AM. * **gaps**: 10:00 AM **not returned as available** (appointment occupies that time) * **slots**: "New Patient" slot **returned as available** (different appointment type doesn't block) * **equal\_slots**: "New Patient" slot **not returned** (any appointment blocks all slot types) **Scenario 2:** A "New Patient" schedule\_slot exists at 10:00 AM. A "New Patient" appointment is already booked at 10:00 AM. * **gaps**: 10:00 AM **not returned as available** (appointment occupies that time) * **slots**: "New Patient" slot **not returned** (same appointment type blocks) * **equal\_slots**: "New Patient" slot **not returned** (any appointment blocks all slot types) **Scenario 3:** A "New Patient" schedule\_slot exists at 10:00 AM. A schedule\_block (e.g., "Lunch") exists at 10:00 AM. * **gaps**: 10:00 AM **not returned as available** (schedule\_block occupies that time) * **slots**: "New Patient" slot **not returned** (schedule\_blocks block slots) * **equal\_slots**: "New Patient" slot **not returned** (schedule\_blocks block all slot types) Use `equal_slots` when multiple parties (e.g., clinic staff and automated systems) are scheduling different appointment types into the same time slots and you need to prevent double-booking. # Create Claim Source: https://docs.usecobalt.com/api-reference/claims/create POST /claims Creates a claim for a given appointment in the connected EMR system. This is an async operation. The endpoint returns `202 Accepted` immediately after queuing the claim, and a webhook event is sent to your registered endpoint once the claim has been created (or has failed) in the EMR. ### Request Parameters #### Required Fields * **appointment\_id** (string, required): The Cobalt appointment ID to create a claim for. The appointment must belong to the authenticated user and have an associated EMR appointment ID. ### Example Request ```bash theme={null} curl -X POST https://api.usecobalt.com/v1/claims \ -H 'Content-Type: application/json' \ -H 'client_id: ci_live_198908HJDKJSH98789OHKJL' \ -H 'client_secret: cs_live_9827hofdsklOYYHJLJh' \ -H 'access_token: 493JKLHIU98789hLKH9HHJH' \ -d '{ "appointment_id": "a1b2c3d4e5f64g7h8i9jk0l1m2n3o4p5" }' ``` ### Example Response ```json theme={null} { "success": true, "message": "Claim creation queued. A webhook event will be sent upon completion.", "claim_id": "f4g5h6i7j8k9l0m1n2o3p4q5r6s7t8u9", "appointment_id": "a1b2c3d4e5f64g7h8i9jk0l1m2n3o4p5", "job_id": 12345 } ``` The response includes: * **claim\_id**: Unique identifier for the queued claim record. Use this to correlate the eventual webhook event. * **appointment\_id**: Echo of the appointment the claim was queued against. * **job\_id**: Job execution identifier for tracking the async operation. ### Error Responses #### Missing Required Field ```json theme={null} { "success": false, "message": "'appointment_id' is required." } ``` #### Invalid Appointment ID Format ```json theme={null} { "success": false, "message": "Invalid 'appointment_id' format. Please provide a valid UUID." } ``` #### Appointment Missing EMR ID Returned if the appointment exists but has not yet been synced with an `emr_appointment_id`. ```json theme={null} { "success": false, "message": "Appointment is missing emr_appointment_id; cannot create claim." } ``` #### Appointment Not Found ```json theme={null} { "success": false, "message": "Appointment not found." } ``` #### Duplicate Claim Returned if a claim for this appointment is already in-flight (`pending_create`) or has already been completed. Failed claims do not block retries. ```json theme={null} { "success": false, "message": "A claim for this appointment is already pending or completed." } ``` ### Webhook Notifications When the claim creation is complete, we will send a webhook to your registered endpoint. Here are examples of what those webhook payloads will look like: #### Success ```json theme={null} { "id": "evt_1J9X2q2eZvKYlo2CluR9g9gV", "access_token_reference_id": "user_1J9X2q2eZvKYlo2Cxyz", "object": "event", "created": "2026-05-22T10:30:00Z", "type": "claim.created", "job_id": "12345", "data": { "claim_id": "f4g5h6i7j8k9l0m1n2o3p4q5r6s7t8u9", "appointment_id": "a1b2c3d4e5f64g7h8i9jk0l1m2n3o4p5", "patient_mrn": "MRN-12345", "emr_claim_id": "100234", "emr_appointment_id": "500100", "emr_patient_id": "40210" } } ``` #### Failure ```json theme={null} { "id": "evt_1J9X2q2eZvKYlo2CluR9g9gW", "access_token_reference_id": "user_1J9X2q2eZvKYlo2Cxyz", "object": "event", "created": "2026-05-22T10:35:00Z", "type": "claim.failed", "job_id": "12345", "data": { "claim_id": "f4g5h6i7j8k9l0m1n2o3p4q5r6s7t8u9", "appointment_id": "a1b2c3d4e5f64g7h8i9jk0l1m2n3o4p5", "patient_mrn": "MRN-12345", "failure_reason": "Failed to create claim" } } ``` # Get Claims Source: https://docs.usecobalt.com/api-reference/claims/get GET /claims Returns paginated claims with line items, payments, insurance details, and billing activity for a given date range. ### Example Request ```bash theme={null} curl -X GET https://api.usecobalt.com/v1/claims \ -H 'Content-Type: application/json' \ -H 'client_id: ci_live_198908HJDKJSH98789OHKJL' \ -H 'client_secret: cs_live_9827hofdsklOYYHJLJh' \ -H 'access_token: 493JKLHIU98789hLKH9HHJH' \ -G \ --data-urlencode "start_date=2025-08-01" \ --data-urlencode "end_date=2025-09-01" ``` ### Example Response ```json theme={null} { "success": true, "claims": [ { "id": "a1b2c3d4e5f6789012345678abcdef90", "emr_claim_id": "100234", "emr_appointment_id": "500100", "service_date": "2025-08-01", "claim_date": "2025-08-08", "claim_status": "48CL", "claim_status_description": "Closed (48-hour rule)", "claim_type": "0", "void_flag": false, "emr_patient_id": "40210", "patient_name": "DOE, JANE, M", "emr_provider_id": "10052", "provider_name": "Smith, Robert", "total_charge": "350.00", "total_payment": "280.00", "net_payment": "280.00", "balance": "20.00", "net_adjustment": "50.00", "patient_responsibility": "20.00", "insurances": [ { "insurance_id": "101", "insurance_name": "Blue Cross Blue Shield", "insurance_type": "C1", "claim_indicator": "P", "claim_indicator_description": "Primary", "sequence_number": "1", "subscriber_name": "DOE, JANE, M", "subscriber_number": "XYZ123456789", "subscriber_dob": "1975-06-15", "subscriber_relation": "1", "group_name": "ACME CORP", "group_number": "GRP-98765", "provider_number": "BCBS1", "payment_source": "16", "assign_benefits": "Y", "insurance_address_1": "PO BOX 12345", "insurance_address_2": "", "insurance_city": "CHICAGO", "insurance_state": "IL", "insurance_zip": "60601", "medicaid_id": "" }, { "insurance_id": "202", "insurance_name": "Medicare Part B", "insurance_type": "C1", "claim_indicator": "S", "claim_indicator_description": "Secondary", "sequence_number": "2", "subscriber_name": "DOE, JANE, M", "subscriber_number": "1EG4TE5MK72", "subscriber_dob": "1975-06-15", "subscriber_relation": "1", "group_name": "", "group_number": "", "provider_number": "MED01", "payment_source": "MB", "assign_benefits": "Y", "insurance_address_1": "CMS CLAIMS", "insurance_address_2": "PO BOX 5678", "insurance_city": "BALTIMORE", "insurance_state": "MD", "insurance_zip": "21244", "medicaid_id": "" } ], "balances": { "invoice_amount": "350.00", "total_payment": "280.00", "net_payment": "280.00", "balance": "20.00", "patient_balance": "20.00", "patient_payment": "0.00", "patient_responsibility": "20.00", "on_plan": "false" }, "icd_codes": ["M54.5", "Z96.641"], "void_claim": { "void_to_claim_id": "-1", "void_from_claim_id": "-1", "copy_to_claim_id": "-1", "copy_from_claim_id": "-1" }, "submission_history": { "created_by": "Johnson, Sarah", "entries": [], "auto_claim_data": { "isUserInactive": "false", "schedulerJobId": "-1", "isClaimCreatedByJob": "false" } }, "billing_activity_log": [ { "date": "2025-08-19 14:57:00", "action": "Modified", "category": "Claim Status", "notes": "Changed From: Pending With Errors to Closed Zero Balance", "user_name": "Sarah Johnson (sjohnson)" }, { "date": "2025-08-08 15:40:40", "action": "Created", "category": "Insurance", "notes": "Created Insurance [P] [Id: 101] Blue Cross Blue Shield", "user_name": "Sarah Johnson (sjohnson)" }, { "date": "2025-08-08 15:40:00", "action": "Modified", "category": "Claim Status", "notes": "Changed From: to Pending", "user_name": "Sarah Johnson (sjohnson)" } ], "billing_notes": "Eligibility verified 2025-08-01: Primary active, deductible met, copay $20. Secondary Medicare active.", "line_items": [ { "id": "f1e2d3c4b5a6789012345678", "line_number": 1, "cpt_code": "99213", "description": "Office visit, established patient", "modifiers": ["25"], "units": 1, "billed_amount": 150.00, "allowed_amount": 120.00, "billed_unit_fee": 150.00, "icd_pointers": ["1", "2"], "place_of_service": "11", "type_of_service": null, "service_date_start": "2025-08-01", "service_date_end": "2025-08-01", "rendering_provider_npi": "1234567890", "ordering_provider_npi": null, "referring_provider_npi": null }, { "id": "a9b8c7d6e5f4321098765432", "line_number": 2, "cpt_code": "20610", "description": "Arthrocentesis, major joint", "modifiers": [], "units": 1, "billed_amount": 200.00, "allowed_amount": 160.00, "billed_unit_fee": 200.00, "icd_pointers": ["1"], "place_of_service": "11", "type_of_service": null, "service_date_start": "2025-08-01", "service_date_end": "2025-08-01", "rendering_provider_npi": "1234567890", "ordering_provider_npi": null, "referring_provider_npi": null } ], "payments": [ { "id": "d4c3b2a1f6e5789012345678", "emr_payment_id": "55001", "check_number": "EFT-20250825-001", "payment_date": "2025-08-25", "amount": 280.00, "payment_type": "Insurance", "payer_name": "Blue Cross Blue Shield", "payer_type": "Insurance", "adjustment": 50.00, "deductible": 0.00, "coinsurance": 20.00, "allowed": 280.00, "member_balance": 20.00, "withheld": 0.00, "era_claim_status": "Processed as Primary", "era_claim_status_code": "1", "era_payer_name": "Blue Cross Blue Shield", "era_check_amount": 280.00, "era_adjustment_codes": null } ], "synced_at": "2025-08-31T04:40:00.002Z", "created_at": "2025-08-31T04:40:00.002Z", "updated_at": "2025-08-31T04:40:00.002Z" } ], "pagination": { "page": 1, "page_size": 100, "total_count": 45, "total_pages": 1 } } ``` # Get Claim by ID Source: https://docs.usecobalt.com/api-reference/claims/get-by-id GET /claims/{id} Returns a single claim with its line items, payments, and billing details by its Cobalt claim ID, for example the data.id from a claim webhook. ### Example Request ```bash theme={null} curl -X GET https://api.usecobalt.com/v1/claims/a1b2c3d4e5f6789012345678abcdef90 \ -H 'client_id: ci_live_198908HJDKJSH98789OHKJL' \ -H 'client_secret: cs_live_9827hofdsklOYYHJLJh' \ -H 'access_token: 493JKLHIU98789hLKH9HHJH' ``` ### Example Response ```json theme={null} { "success": true, "claim": { "id": "a1b2c3d4e5f6789012345678abcdef90", "emr_claim_id": "100234", "emr_appointment_id": "500100", "service_date": "2025-08-01", "claim_date": "2025-08-08", "claim_status": "48CL", "claim_status_description": "Closed (48-hour rule)", "claim_type": "0", "void_flag": false, "emr_patient_id": "40210", "patient_name": "DOE, JANE, M", "emr_provider_id": "10052", "provider_name": "Smith, Robert", "total_charge": "350.00", "total_payment": "280.00", "net_payment": "280.00", "balance": "20.00", "net_adjustment": "50.00", "patient_responsibility": "20.00", "icd_codes": ["M54.5", "Z96.641"], "line_items": [ { "id": "f1e2d3c4b5a6789012345678", "line_number": 1, "cpt_code": "99213", "description": "Office visit, established patient", "modifiers": ["25"], "units": 1, "billed_amount": 150.00, "allowed_amount": 120.00, "icd_pointers": ["1", "2"], "place_of_service": "11", "service_date_start": "2025-08-01", "service_date_end": "2025-08-01", "rendering_provider_npi": "1234567890" } ], "payments": [ { "id": "d4c3b2a1f6e5789012345678", "emr_payment_id": "55001", "check_number": "EFT-20250825-001", "payment_date": "2025-08-25", "amount": 280.00, "payment_type": "Insurance", "payer_name": "Blue Cross Blue Shield", "adjustment": 50.00, "coinsurance": 20.00, "allowed": 280.00, "era_claim_status": "Processed as Primary" } ], "synced_at": "2025-08-31T04:40:00.002Z", "created_at": "2025-08-31T04:40:00.002Z", "updated_at": "2025-08-31T04:40:00.002Z" } } ``` # Add Diagnosis Codes Source: https://docs.usecobalt.com/api-reference/codes/add POST /appointments/{appointment_id}/codes Adds ICD-10 diagnosis codes to an existing appointment's encounter. This is an asynchronous operation. The request returns a `job_id` immediately, and a webhook is sent when the operation completes. Codes already on the encounter are skipped, so re-sending a code is safe. Only ICD-10 diagnosis codes are supported today; CPT codes are not. Each code is `{ "type": "icd10", "code": "..." }`. The dot in a code is optional (`E119` and `E11.9` are equivalent). Send one or many codes in a single request. ### Example Request ```bash theme={null} curl -X POST https://api.usecobalt.com/v1/appointments/APPT-12345/codes \ -H 'Content-Type: application/json' \ -H 'client_id: ci_live_198908HJDKJSH98789OHKJL' \ -H 'client_secret: cs_live_9827hofdsklOYYHJLJh' \ -H 'access_token: 493JKLHIU98789hLKH9HHJH' \ -d '{ "codes": [ { "type": "icd10", "code": "E11.9" }, { "type": "icd10", "code": "I10" } ] }' ``` ### Example Response ```json theme={null} { "success": true, "message": "Codes operation in progress. A webhook event will be sent upon completion.", "job_id": "12345" } ``` ### Webhook On completion we send a `codes.added` event (or `codes.failed` on error). `icd` reports which codes were applied versus skipped as duplicates, not matched in the EMR catalog, or rejected as invalid for the encounter date. ```json theme={null} { "id": "evt_1J9X2q2eZvKYlo2CluR9g9gV", "access_token_reference_id": "user_1J9X2q2eZvKYlo2Cxyz", "object": "event", "created": "2025-10-09T10:30:00Z", "type": "codes.added", "job_id": "12345", "data": { "appointment_id": "APPT-12345", "operation": "add", "icd": { "added": ["E11.9", "I10"], "skipped": [], "unmatched": [], "invalid": [] } } } ``` See [Operation Events](/docs/webhooks/operation-events) for the failure payload and error codes. # Remove Diagnosis Codes Source: https://docs.usecobalt.com/api-reference/codes/remove DELETE /appointments/{appointment_id}/codes Removes ICD-10 diagnosis codes from an existing appointment's encounter. This is an asynchronous operation. The request returns a `job_id` immediately, and a webhook is sent when the operation completes. Codes that are not on the encounter are ignored. The body is the same shape as [Add Diagnosis Codes](/api-reference/codes/add): each code is `{ "type": "icd10", "code": "..." }`, and you can remove one or many in a single request. ### Example Request ```bash theme={null} curl -X DELETE https://api.usecobalt.com/v1/appointments/APPT-12345/codes \ -H 'Content-Type: application/json' \ -H 'client_id: ci_live_198908HJDKJSH98789OHKJL' \ -H 'client_secret: cs_live_9827hofdsklOYYHJLJh' \ -H 'access_token: 493JKLHIU98789hLKH9HHJH' \ -d '{ "codes": [ { "type": "icd10", "code": "I10" } ] }' ``` ### Example Response ```json theme={null} { "success": true, "message": "Codes operation in progress. A webhook event will be sent upon completion.", "job_id": "12346" } ``` ### Webhook On completion we send a `codes.removed` event (or `codes.failed` on error). `not_found` lists codes that were not on the encounter. ```json theme={null} { "id": "evt_1J9X2q2eZvKYlo2CluR9g9gW", "access_token_reference_id": "user_1J9X2q2eZvKYlo2Cxyz", "object": "event", "created": "2025-10-09T10:35:00Z", "type": "codes.removed", "job_id": "12346", "data": { "appointment_id": "APPT-12345", "operation": "remove", "icd": { "removed": ["I10"], "not_found": [] } } } ``` See [Operation Events](/docs/webhooks/operation-events) for the failure payload and error codes. # Fetch Document Source: https://docs.usecobalt.com/api-reference/documents/fetch POST /documents/fetch Fetch a document's file from the EMR. Queues an on-demand fetch of a document's file from the EMR. This is asynchronous: the endpoint returns a `job_id`, Cobalt retrieves the file from the EMR and stores it, then sends a `document.live_fetch_completed` webhook. Retrieve the file with [GET /documents/](/api-reference/documents/get). Depending on the EMR, the request takes either `document_ids` (a batch of 1-10 Cobalt document IDs) or a single `document_id`. See the request body below for which field your EMR uses. ## Getting document IDs Use the IDs Cobalt returns for EMR documents: * `documents[].id` on each patient in a [patient live fetch](/api-reference/patients/fetch): a file in the patient's chart. * `encounters[].visit_note_document_id` on a patient live fetch, or `visit_note_document_id` on each appointment in an [appointment live fetch](/api-reference/appointments/fetch): the encounter's visit note, delivered as a PDF. Cobalt document IDs are UUIDs, accepted with or without dashes. They are stable: the same EMR document always has the same ID, so you can store them. ## Flow 1. `POST /documents/fetch` returns `202` and a `job_id`. 2. Cobalt fetches each file from the EMR and stores it. Visit notes are rendered to PDF. 3. A `document.live_fetch_completed` webhook is sent with a result per document. 4. `GET /documents/{id}` returns the file. ## Example Request ```bash theme={null} curl -X POST "https://api.usecobalt.com/v1/documents/fetch" \ -H "client_id: your_client_id" \ -H "client_secret: your_client_secret" \ -H "access_token: your_access_token" \ -H "Content-Type: application/json" \ -d '{"document_ids": ["3f1c2b7e9d4a4c6f8b2e1a0d5c7e9f12", "8a7b6c5d4e3f4a2b9c1d0e9f8a7b6c5d"]}' ``` ## Webhook With `document_ids`, `data.documents` has one result per requested document. A document that could not be fetched has `success: false` and a `message`; the other documents in the batch are not affected. ```json theme={null} { "id": "evt_1J9X2q2eZvKYlo2Cmnopqr", "job_id": "job_1J9X2q2eZvKYlo2Cmnopqr", "object": "event", "created": "2026-10-07T22:54:50Z", "type": "document.live_fetch_completed", "data": { "success": true, "documents": [ { "id": "3f1c2b7e9d4a4c6f8b2e1a0d5c7e9f12", "success": true, "document_type": "visit_note", "file_name": "visit_note_159820.pdf", "mime_type": "application/pdf", "byte_size": 41040, "patient_mrn": "15322" }, { "id": "8a7b6c5d4e3f4a2b9c1d0e9f8a7b6c5d", "success": false, "message": "Document not found." } ] } } ``` ## Status Codes * **202**: Fetch accepted; results are delivered via webhook and files are retrievable at `GET /documents/{id}`. * **400**: Missing or invalid `document_ids` / `document_id`, more than 10 `document_ids`, or the connected EMR is not supported. * **404**: One or more `document_ids` are not documents of this linked account. The message lists them as sent. # Get Document Source: https://docs.usecobalt.com/api-reference/documents/get GET /documents/{id} Download a document by its Cobalt ID. Returns the raw file bytes for a document. The response is the file itself — the `Content-Type` header carries the document's MIME type and `Content-Disposition` carries the original filename. Use it to retrieve: * PDFs referenced by `document_ids` on a referral. * Documents fetched with [POST /documents/fetch](/api-reference/documents/fetch): chart files (`documents[].id` on a patient live fetch) and visit notes (`visit_note_document_id`). A document from a patient or appointment live fetch has no file until you fetch it. Until then this endpoint returns `409`. ## Path Parameters * **id** (string, required): The Cobalt document ID (UUID, with or without dashes). ## Example Request ```bash theme={null} curl -X GET "https://api.usecobalt.com/v1/documents/b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5" \ -H "client_id: your_client_id" \ -H "client_secret: your_client_secret" \ -H "access_token: your_access_token" \ --output document.pdf ``` ## Response On success, the response body is the raw file. Relevant headers: * **Content-Type**: The document's MIME type (typically `application/pdf`). * **Content-Disposition**: `attachment; filename=""`. * **Content-Length**: The file size in bytes. On failure, the response is JSON: ```json theme={null} { "success": false, "message": "Document not found" } ``` ## Status Codes * **200**: Document returned successfully. * **404**: Document not found. * **409**: The document's file has not been fetched yet. Request it with `POST /documents/fetch`, then retry after the `document.live_fetch_completed` webhook. ## Notes * Access is scoped to your organization — only documents associated with your linked accounts are returned. * Documents are stored as they were received from the EMR. PDFs are returned unmodified. # Upload Document Source: https://docs.usecobalt.com/api-reference/documents/upload POST /documents Upload a document to a patient's chart. Documents are processed asynchronously. You'll receive a webhook notification when the upload is complete or if there was an error. ### Example Request ```bash theme={null} curl -X POST https://api.usecobalt.com/v1/documents \ -H 'Content-Type: multipart/form-data' \ -H 'client_id: ci_live_198908HJDKJSH98789OHKJL' \ -H 'client_secret: cs_live_9827hofdsklOYYHJLJh' \ -H 'access_token: 493JKLHIU98789hLKH9HHJH' \ -F 'folder_name=MedicalRecords' \ -F 'patient_mrn=123456' \ -F 'location_id=20' \ -F 'tags=Estimate' \ -F 'tags=Referral' \ -F 'document=@/path/to/document.pdf' ``` To attach the document to an existing referral or order instead of a folder, send `association_type` and `association_id` (the Cobalt `id` from `GET /v1/referrals` or `GET /v1/orders`) in place of `folder_name`: ```bash theme={null} curl -X POST https://api.usecobalt.com/v1/documents \ -H 'Content-Type: multipart/form-data' \ -H 'client_id: ci_live_198908HJDKJSH98789OHKJL' \ -H 'client_secret: cs_live_9827hofdsklOYYHJLJh' \ -H 'access_token: 493JKLHIU98789hLKH9HHJH' \ -F 'patient_mrn=123456' \ -F 'association_type=order' \ -F 'association_id=b1c2d3e4-5678-4abc-9def-0123456789ab' \ -F 'document=@/path/to/document.pdf' ``` To route the document to a staff member for review, send `emr_assigned_to_id`. This is the EMR-native staff ID, not a Cobalt ID. Valid IDs are the `ehr_id` values returned by `GET /v1/staff`. ### Example Response ```json theme={null} { "success": true, "message": "Document uploaded successfully. It will be processed soon.", "document_id": "9988776655443322" } ``` ### Example Success Webhook ```json theme={null} { "id": "123e4567-e89b-12d3-a456-426614174000", "object": "event", "created": "2023-10-01T12:34:56Z", "type": "document.uploaded", "data": { "document_id": "9988776655443322", "patient_mrn": "123456", "folder_name": "MedicalRecords" } } ``` ### Example Failure Webhook ```json theme={null} { "id": "123e4567-e89b-12d3-a456-426614174001", "object": "event", "created": "2023-10-01T12:34:56Z", "type": "document.upload_failed", "data": { "document_id": "9988776655443322", "patient_mrn": "123456", "folder_name": "MedicalRecords", "failure_reason": "Patient not found. Please check the patient MRN and try again." } } ``` # Get Eligibility Benefits Source: https://docs.usecobalt.com/api-reference/eligibility-checks/benefits GET /eligibility-checks/{id}/benefits Returns the per-benefit detail for a single eligibility check. A 271 response breaks down into many benefit lines, one per service type and coverage level: deductibles, copays, coinsurance, out-of-pocket maximums, limitations, and exclusions. Get the check `id` from [Get Eligibility Checks](/api-reference/eligibility-checks/get), then page the benefits here. Narrow results with `benefit_type`. ### Example Request ```bash theme={null} curl -X GET "https://api.usecobalt.com/v1/eligibility-checks/abc123def4567890abcdef1234567890/benefits?benefit_type=Co-Payment&page=1&page_size=100" \ -H 'Content-Type: application/json' \ -H 'client_id: ci_live_198908HJDKJSH98789OHKJL' \ -H 'client_secret: cs_live_9827hofdsklOYYHJLJh' \ -H 'access_token: 493JKLHIU98789hLKH9HHJH' ``` ### Example Response ```json theme={null} { "success": true, "benefits": [ { "line_number": 17, "benefit_type": "Co-Payment", "benefit_type_code": "B", "coverage_level": "IND", "service_type_codes": ["98"], "insurance_type": null, "time_period": null, "amount": 25, "percent": null, "network_indicator": "in-network", "messages": ["Specialist Visit or Evaluation"], "raw_segment": "EB*B*IND*98****25*****Y" } ], "pagination": { "page": 1, "page_size": 100, "total_records": 1, "total_pages": 1 } } ``` ### Query Parameters * **benefit\_type**: Filter by decoded benefit type (e.g. `Deductible`, `Co-Payment`, `Co-Insurance`, `Out of Pocket`). * **page**: Page number (default `1`). * **page\_size**: Results per page (default `100`, max `500`). ### Response Parameters * **line\_number**: Segment order within the 271. * **benefit\_type**: Decoded benefit category, e.g. `Active Coverage`, `Deductible`, `Co-Payment`, `Co-Insurance`, `Out of Pocket`, `Limitations`, `Non-Covered`. * **benefit\_type\_code**: The raw X12 EB01 code. * **coverage\_level**: `IND` (individual) or `FAM` (family). * **service\_type\_codes**: X12 service type codes the benefit applies to. * **insurance\_type**: Insurance type code, when present. * **time\_period**: Decoded time period, e.g. `Calendar Year` or `Remaining`. * **amount**: Monetary amount (deductible, copay, or out-of-pocket). * **percent**: Coinsurance percentage as a decimal (e.g. `0.2` = 20%). * **network\_indicator**: `in-network`, `out-of-network`, `unknown`, or `not-applicable`. * **messages**: Payer free-text notes attached to the benefit line. * **raw\_segment**: The raw X12 EB segment, retained for reference. * **pagination**: `page`, `page_size`, `total_records`, `total_pages`. # Get Eligibility Checks Source: https://docs.usecobalt.com/api-reference/eligibility-checks/get GET /eligibility-checks Returns insurance eligibility checks (270/271) for a clinic. Each eligibility check is one 270/271 verification: a request sent to a payer and the benefits response that came back. This endpoint returns the check **headers**, each with a `benefits_summary` (the headline deductible and out-of-pocket numbers) and a `benefit_count`. To read the full per-benefit detail for a check, use its `id` with [Get Eligibility Benefits](/api-reference/eligibility-checks/benefits). Filter by service-date range, `patient_mrn`, or `coverage_status`. A check whose `coverage_status` is `error` is one the payer could not process (for example a missing provider NPI); the reason is in `status_message`. ### Example Request ```bash theme={null} curl -X GET "https://api.usecobalt.com/v1/eligibility-checks?start_date=2026-06-01&end_date=2026-06-30&coverage_status=active&page=1&page_size=100" \ -H 'Content-Type: application/json' \ -H 'client_id: ci_live_198908HJDKJSH98789OHKJL' \ -H 'client_secret: cs_live_9827hofdsklOYYHJLJh' \ -H 'access_token: 493JKLHIU98789hLKH9HHJH' ``` ### Example Response ```json theme={null} { "success": true, "eligibility_checks": [ { "id": "abc123def4567890abcdef1234567890", "emr_id": "4588", "emr_patient_id": "100482", "patient_name": "Sample, Patient", "patient_dob": "1990-01-01", "emr_encounter_id": "293018", "provider_name": "Pat Provider", "payer_name": "Sample Health Plan", "subscriber_name": "Patient Sample", "subscriber_member_id": "W000000000", "patient_relationship": "self", "plan_name": "Sample PPO Plan", "coverage_status": "active", "status_message": null, "service_date": "2026-06-01", "response_at": "2026-06-01T05:01:00.000Z", "plan_begin_date": "2026-01-01", "plan_end_date": "2026-12-31", "benefits_summary": { "deductible": { "individual": { "total": 2000, "remaining": 0 }, "family": { "total": 4000, "remaining": 1500 } }, "out_of_pocket": { "individual": { "total": 7000, "remaining": 917.76 }, "family": { "total": 10000, "remaining": 6217.76 } } }, "benefit_count": 61, "synced_at": "2026-06-22T15:00:00.000Z" } ], "pagination": { "page": 1, "page_size": 100, "total_records": 1, "total_pages": 1 } } ``` ### Query Parameters * **start\_date** / **end\_date**: Filter by service date (`YYYY-MM-DD`). * **patient\_mrn**: Filter to a single patient by medical record number. * **coverage\_status**: Filter by `active`, `inactive`, `error`, or `unknown`. * **page**: Page number (default `1`). * **page\_size**: Results per page (default `100`, max `500`). ### Response Parameters * **id**: Cobalt's eligibility check identifier. Use this in the benefits endpoint path. * **emr\_id**: The eligibility request identifier as it appears in the EMR. * **emr\_patient\_id** / **patient\_name** / **patient\_dob**: The patient the check was run for. * **emr\_encounter\_id**: The encounter/visit the check is tied to. * **provider\_name** / **payer\_name**: Rendering provider and the payer that responded. * **subscriber\_name** / **subscriber\_member\_id** / **patient\_relationship**: Subscriber details; `patient_relationship` is `self` or `dependent`. * **plan\_name**: Plan description from the 271. * **coverage\_status**: `active`, `inactive`, `error` (payer could not process the request), or `unknown`. * **status\_message**: Payer reject reason when `coverage_status` is `error`. * **service\_date** / **response\_at**: Date of service and when the payer response was received. * **plan\_begin\_date** / **plan\_end\_date**: The plan's effective date range (either may be `null`). * **benefits\_summary**: Headline EOB rollup. `deductible` and `out_of_pocket` are each split by `individual` / `family` into `total` and `remaining` amounts. * **benefit\_count**: Number of benefit lines parsed from the 271. * **pagination**: `page`, `page_size`, `total_records`, `total_pages`. # Create Encounter Source: https://docs.usecobalt.com/api-reference/encounters/create POST /encounters Queues an encounter (an episode visit with form documentation) for creation in the provider's EMR system. An encounter is an episode visit written to the patient's chart together with its form documentation (for example a scored questionnaire such as GAD-7). Encounters are created using an asynchronous processing model, so creating one is not instantaneous. Instead of leaving your POST request hanging until completion, we immediately return a success response if the request is properly formed. We then notify you via a webhook when the encounter has been written to the EMR. This gives you flexibility around your user experience. For example, when you first make the `/encounters` call you can display a **Processing** status to your user, and when you receive the webhook notification you can update that to **Completed**. ### How an encounter is created Processing an encounter runs an ordered chain against the EMR: 1. **Episode** — the encounter is attached to the episode you give in `episode_id`. There is no fallback to another episode. Assign the patient to a team first, as a separate step, to open the episode. 2. **Encounter type** — the `encounter_type` is resolved against the client's live billing matrix. An encounter type that is not billable for the client fails the encounter (see [Failure](#failure)). 3. **Form documentation** — the `answers` you supply are validated against the synced form definition (`form_id`) and written on the encounter. For a scored questionnaire, per-question scores are entered and totals and risk bands are derived by the EMR. 4. **Sign and submit** — the encounter is signed with the service date (`encounter_date`), start and end times, and location you supplied. 5. **Read-back** — after signing, the encounter is re-read from the EMR to confirm it persisted and to read its status and approval state before the success webhook is sent. ### Request Parameters #### Required Fields * **patient\_mrn** (string, required): The patient's medical record number. The patient must already be synced to Cobalt. * **episode\_id** (string, required): The episode the encounter is created under. Use the exact id from the patient episode payload. * **encounter\_type** (string, required): The encounter type to add, by name or numeric visit-type id (for example `"GAD-7"`). Must be billable for the client — that is, present in the client's billing matrix. * **form\_id** (string, required): EMR form id whose answers you are providing — the `emr_form_id` of a form from [`GET /v1/forms`](/api-reference/forms/get). Required when the encounter type documents a form. * **answers** (object, required): Form answers keyed by EMR question id (the `emrQuestionId` of each question in the form definition — see [`GET /v1/forms/{id}`](/api-reference/forms/get-by-id)). For a scored questionnaire, provide the numeric score for each scored question; totals and risk bands are derived automatically. Answers are validated against the synced form definition before the encounter is written. #### Optional Fields * **encounter\_date** (string, optional): Encounter (service) date in `YYYY-MM-DD` format, in the practice's local time. Defaults to today. Cannot be in the future. * **start\_time** (string, optional): Service start as an ISO 8601 date-time (for example `"2026-10-05T14:00:00-04:00"`). A value without an offset is read in the practice's local time. Sets the service date; if you also send `encounter_date`, it must be the same date. Cannot be in the future. Defaults to the current time. * **end\_time** (string, optional): Service end as an ISO 8601 date-time, on the same day as `start_time` and after it. Defaults to one hour after `start_time`. Requires `start_time`. * **location** (string, optional): Service location, by EMR location id or exact name from the Location list on the EMR's service form (for example `"Broadway Office"`). Defaults to the EMR's default location for the service. * **callback\_urls** (array of strings, optional): URLs to receive the completion webhook for this encounter, in addition to your account webhook. ### Example Request ```bash theme={null} curl -X POST https://api.usecobalt.com/v1/encounters \ -H 'Content-Type: application/json' \ -H 'client_id: ci_live_198908HJDKJSH98789OHKJL' \ -H 'client_secret: cs_live_9827hofdsklOYYHJLJh' \ -H 'access_token: 493JKLHIU98789hLKH9HHJH' \ -d '{ "patient_mrn": "1234567", "episode_id": "32340", "encounter_type": "GAD-7", "form_id": "392", "encounter_date": "2026-08-26", "start_time": "2026-08-26T14:00:00-04:00", "end_time": "2026-08-26T15:00:00-04:00", "location": "Broadway Office", "answers": { "453149": "3", "453150": "2", "453156": "Very difficult" } }' ``` ### Example Response ```json theme={null} { "success": true, "message": "Encounter processing. A webhook event will be sent upon completion.", "encounter_id": "123e4567e89b12d3a456426614174000", "job_id": 12345 } ``` The response includes: * **encounter\_id**: Cobalt encounter ID (UUID without dashes). This is echoed back in the webhook payload so you can correlate the two. * **job\_id**: Job execution identifier for tracking the async operation. ### Error Responses #### Missing Required Field ```json theme={null} { "success": false, "message": "Missing required field: patient_mrn" } ``` #### Answers Not An Object ```json theme={null} { "success": false, "message": "answers must be an object keyed by EMR question id." } ``` #### Invalid Encounter Date ```json theme={null} { "success": false, "message": "encounter_date must be a valid date (YYYY-MM-DD)." } ``` ```json theme={null} { "success": false, "message": "encounter_date cannot be in the future." } ``` #### Invalid Service Times ```json theme={null} { "success": false, "message": "end_time must be after start_time." } ``` Other messages: `start_time must be an ISO 8601 date-time (e.g. 2026-10-05T14:00:00-04:00).` (and the same for `end_time`), `end_time requires start_time.`, `end_time must be on the same day as start_time.`, `encounter_date must match the date of start_time ().`, and `start_time cannot be in the future.` #### User Not Found ```json theme={null} { "success": false, "message": "User not found." } ``` This indicates an issue with your access token. #### Unsupported EMR ```json theme={null} { "success": false, "message": "Creating encounters is not supported for [EMR Name]." } ``` Your EMR system may not currently support encounter creation. ### Webhook Notifications When the encounter has finished processing, we send a webhook to your registered endpoint. #### Success ```json theme={null} { "id": "", "access_token_reference_id": "", "object": "event", "created": "2026-08-26T10:30:00.000Z", "type": "encounter.created", "data": { "encounter_id": "123e4567e89b12d3a456426614174000", "emr_encounter_id": "927341", "emr_episode_id": "88214", "emr_status": "COMPLETED", "approved": false, "patient_mrn": "1234567" } } ``` The webhook data includes: * **encounter\_id**: The Cobalt encounter ID you received in the response. * **emr\_encounter\_id**: The encounter id in the EMR. * **emr\_episode\_id**: The episode the encounter was written under. * **emr\_status**: The encounter status in the EMR after signing (for example `COMPLETED`). `null` if the status could not be read. * **approved**: Whether the EMR approved the encounter on signing. Some EMR configurations approve an encounter automatically and others leave it unapproved; `false` means it still needs approval: approve it with [`PATCH /v1/encounters/{id}`](/api-reference/encounters/update). `null` if the approval state could not be read. * **patient\_mrn**: The patient's medical record number. #### Failure ```json theme={null} { "id": "", "access_token_reference_id": "", "object": "event", "created": "2026-08-26T10:35:00.000Z", "type": "encounter.failed", "data": { "encounter_id": "123e4567e89b12d3a456426614174000", "emr_encounter_id": null, "patient_mrn": "1234567", "failure_reason": "Encounter type 'GAD-7' not available for client 1234567 (not in billing matrix)" } } ``` Transient problems are retried automatically and do not emit a webhook until they are resolved or the attempts are exhausted. A `encounter.failed` event is sent once, when the failure is permanent. Common `failure_reason` values: * `Encounter type '' not available for client (not in billing matrix)` — the encounter type is not billable for the client. * `Missing required answers: ` — one or more required form questions were not answered. * `Location '' is not in the service Location list for client .` — the `location` does not match a location id or name in the EMR's service Location list. # Update Encounter Source: https://docs.usecobalt.com/api-reference/encounters/update PATCH /encounters/{id} Approves an existing encounter in the provider's EMR system. Some EMR configurations approve an encounter automatically when it is signed; others leave it unapproved. The [`encounter.created`](/api-reference/encounters/create#success) webhook tells you which: when `approved` is `false`, call this endpoint to approve the encounter. Updates are processed asynchronously. We return a success response as soon as the request is accepted, then send a webhook when the update has been applied in the EMR. ### Path Parameters * **id** (string, required): The Cobalt `encounter_id` returned when the encounter was created. ### Request Parameters #### Required Fields * **approved** (boolean, required): Must be `true`. Approves the encounter. Unapproving is not supported. If the encounter was already approved in the EMR before your request (for example, approved by a user in the EMR), nothing is changed and the `encounter.updated` webhook has `already_approved: true`. #### Optional Fields * **callback\_urls** (array of strings, optional): URLs to receive the completion webhook for this update, in addition to your account webhook. ### Example Request ```bash theme={null} curl -X PATCH https://api.usecobalt.com/v1/encounters/123e4567e89b12d3a456426614174000 \ -H 'Content-Type: application/json' \ -H 'client_id: ci_live_198908HJDKJSH98789OHKJL' \ -H 'client_secret: cs_live_9827hofdsklOYYHJLJh' \ -H 'access_token: 493JKLHIU98789hLKH9HHJH' \ -d '{ "approved": true }' ``` ### Example Response ```json theme={null} { "success": true, "message": "Encounter update processing. A webhook event will be sent upon completion.", "encounter_id": "123e4567e89b12d3a456426614174000", "job_id": 12346 } ``` ### Error Responses #### Missing Approved ```json theme={null} { "success": false, "message": "Missing required field: approved (must be true)." } ``` #### Encounter Not Found ```json theme={null} { "success": false, "message": "Encounter with id '123e4567e89b12d3a456426614174000' not found." } ``` #### Encounter Not Created Yet ```json theme={null} { "success": false, "message": "Encounter has not been created in the EMR yet. Wait for the encounter.created webhook before updating it." } ``` #### Update Already In Progress ```json theme={null} { "success": false, "message": "An update is already in progress for this encounter. Please wait for it to complete before submitting another update." } ``` #### Unsupported EMR ```json theme={null} { "success": false, "message": "Updating encounters is not supported for [EMR Name]." } ``` ### Webhook Notifications #### Success ```json theme={null} { "id": "", "access_token_reference_id": "", "object": "event", "created": "2026-10-06T10:30:00.000Z", "type": "encounter.updated", "job_id": "12346", "data": { "encounter_id": "123e4567e89b12d3a456426614174000", "emr_encounter_id": "927341", "emr_status": "COMPLETED", "approved": true, "already_approved": false, "patient_mrn": "1234567" } } ``` The webhook data includes: * **encounter\_id**: The Cobalt encounter ID. * **emr\_encounter\_id**: The encounter id in the EMR. * **emr\_status**: The encounter status in the EMR. * **approved**: `true` once the encounter is approved. * **already\_approved**: `true` if the encounter was already approved in the EMR before your request, so nothing was changed. `false` if this request approved it. * **patient\_mrn**: The patient's medical record number. #### Failure ```json theme={null} { "id": "", "access_token_reference_id": "", "object": "event", "created": "2026-10-06T10:35:00.000Z", "type": "encounter.update_failed", "job_id": "12346", "data": { "encounter_id": "123e4567e89b12d3a456426614174000", "emr_encounter_id": "927341", "patient_mrn": "1234567", "failure_reason": "Service 927341 cannot be approved by this account (no Approve button on the visit view)." } } ``` A failed update is not retried automatically; send the request again once the cause is fixed. Common `failure_reason` values: * `Service cannot be approved by this account (no Approve button on the visit view).` — the connected EMR user does not have rights to approve this encounter. * `Approve did not persist for service ...` — the EMR did not record the approval. Retry the request. # Get Fee Schedules Source: https://docs.usecobalt.com/api-reference/fee-schedules/get GET /fee-schedules Returns the fee schedules configured for a clinic. A clinic can have multiple fee schedules (for example a Master schedule, a Medicare schedule, and a Self-Pay schedule). The CPT/HCPCS catalog is shared across schedules, so each code appears in every schedule with that schedule's own charge and allowed amounts. This endpoint returns the schedule **headers** with an item count. To read the per-code pricing for a schedule, use its `id` with [Get Fee Schedule Items](/api-reference/fee-schedules/items). ### Example Request ```bash theme={null} curl -X GET https://api.usecobalt.com/v1/fee-schedules \ -H 'Content-Type: application/json' \ -H 'client_id: ci_live_198908HJDKJSH98789OHKJL' \ -H 'client_secret: cs_live_9827hofdsklOYYHJLJh' \ -H 'access_token: 493JKLHIU98789hLKH9HHJH' ``` ### Example Response ```json theme={null} { "success": true, "fee_schedules": [ { "id": "abc123def4567890abcdef1234567890", "emr_id": "2", "name": "2014 Medicare Fee Schedule", "description": "Medicare Fee Schedule - 2014", "sub_name": "Medicare Fee Schedule - 2014", "is_master": false, "taxable": false, "effective_from": "2014-01-01", "effective_to": "2034-12-31", "item_count": 2624 }, { "id": "def456abc7890123def456abc7890123", "emr_id": "1", "name": "Master Fee Schedule", "description": "", "sub_name": "", "is_master": true, "taxable": false, "effective_from": null, "effective_to": null, "item_count": 2624 } ] } ``` ### Response Parameters * **id**: Cobalt's fee schedule identifier. Use this in the items endpoint path. * **emr\_id**: The fee schedule identifier as it appears in the EMR. * **name** / **description** / **sub\_name**: Labels from the EMR. * **is\_master**: Whether this is the master fee schedule. * **taxable**: Whether the schedule is marked taxable. * **effective\_from** / **effective\_to**: The schedule's effective date range (either may be `null`). Useful for cross-referencing a historical claim against the schedule that was in effect at the time. * **item\_count**: Number of priced codes in the schedule. # Get Fee Schedule Items Source: https://docs.usecobalt.com/api-reference/fee-schedules/items GET /fee-schedules/{feeScheduleId}/items Returns a page of per-code pricing for a single fee schedule. A fee schedule contains the full CPT/HCPCS catalog (thousands of codes), so its items are paginated. Get the `feeScheduleId` from [Get Fee Schedules](/api-reference/fee-schedules/get), then page through the codes here. You can narrow results with `search` (code or description) and `validity`. ### Example Request ```bash theme={null} curl -X GET "https://api.usecobalt.com/v1/fee-schedules/abc123def4567890abcdef1234567890/items?page=1&page_size=100&search=99213&validity=valid" \ -H 'Content-Type: application/json' \ -H 'client_id: ci_live_198908HJDKJSH98789OHKJL' \ -H 'client_secret: cs_live_9827hofdsklOYYHJLJh' \ -H 'access_token: 493JKLHIU98789hLKH9HHJH' ``` ### Example Response ```json theme={null} { "success": true, "items": [ { "item_id": "279840", "code": "49082", "name": "ABD PARACENTESIS", "fee": 287.0, "allowed_fee": 191.28, "cost": 0.0, "patient_portion": 0.0, "bill_to_patient": false, "modifiers": [], "pos": null, "tos": null, "ndc": { "code": null, "units": null, "uom": null, "unit_price": null }, "exclude_cpt": false, "validity": "valid" } ], "pagination": { "page": 1, "page_size": 100, "total_records": 2624, "total_pages": 27 } } ``` ### Query Parameters * **page**: Page number (default `1`). * **page\_size**: Items per page (default `100`, max `500`). * **search**: Filter by code or description (case-insensitive). * **validity**: Filter to `valid` or `invalid` codes. ### Response Parameters * **item\_id**: EMR item identifier, stable per code within the schedule. * **code**: CPT/HCPCS code. * **name**: Code description. * **fee**: Billed/charge amount. **allowed\_fee**: Contracted allowed amount. These differ per schedule for the same code. * **cost** / **patient\_portion**: Cost and patient-portion amounts. * **bill\_to\_patient** / **exclude\_cpt**: Billing flags. * **modifiers**: Procedure modifiers (only non-empty slots are returned). * **pos** / **tos**: Place of service / type of service. * **ndc**: National Drug Code details (`code`, `units`, `uom`, `unit_price`). * **validity**: `valid` or `invalid`. Invalid codes are retained so historical claims can still be cross-referenced. * **pagination**: `page`, `page_size`, `total_records`, `total_pages`. # Sync Forms Source: https://docs.usecobalt.com/api-reference/forms/fetch POST /forms/fetch Queues a sync of the connected EMR's form definitions into the Cobalt cache. Queues a background sync that scrapes the connected EMR's form definitions and upserts them into the Cobalt cache. The request returns immediately with `202 Accepted`; a `forms.synced` webhook is sent when the sync completes. The cached results are then served by [`GET /v1/forms`](/api-reference/forms/get) and [`GET /v1/forms/{id}`](/api-reference/forms/get-by-id). A daily scheduler drives the routine cadence — use this endpoint for on-demand or onboarding refreshes. ### Concurrency Only one sync may run per account at a time. If a sync is already in progress, the request returns `409` with the id of the in-flight job so you can back off rather than queueing a duplicate. ### Request Parameters #### Optional Fields * **callback\_urls** (array of strings, optional): URLs to receive the `forms.synced` webhook, in addition to your account webhook. ### Example Request ```bash theme={null} curl -X POST https://api.usecobalt.com/v1/forms/fetch \ -H 'Content-Type: application/json' \ -H 'client_id: ci_live_198908HJDKJSH98789OHKJL' \ -H 'client_secret: cs_live_9827hofdsklOYYHJLJh' \ -H 'access_token: 493JKLHIU98789hLKH9HHJH' \ -d '{}' ``` ### Example Response ```json theme={null} { "success": true, "status": "queued", "message": "Forms sync started. A forms.synced webhook will be sent when it completes.", "job_execution_id": "123e4567e89b12d3a456426614174000" } ``` ### Error Responses #### Sync Already Running ```json theme={null} { "success": false, "status": "running", "message": "A forms sync is already in progress for this account. Please try again once it completes.", "job_execution_id": "123e4567e89b12d3a456426614174000" } ``` Returned with `409`. Wait for the in-flight sync (identified by `job_execution_id`) to finish before retrying. #### Unsupported EMR ```json theme={null} { "success": false, "message": "Forms sync is not supported for [EMR Name]." } ``` #### User Not Found ```json theme={null} { "success": false, "message": "User not found." } ``` This indicates an issue with your access token. ### Webhook Notification When the sync finishes, we send a `forms.synced` webhook to your registered endpoint. Read the refreshed forms with [`GET /v1/forms`](/api-reference/forms/get). # List Forms Source: https://docs.usecobalt.com/api-reference/forms/get GET /forms Returns cached form metadata for the account. Returns the metadata for every form Cobalt has cached for your account — id, name, type, version, and status. The full form definition (categories, questions, answers) is not included here; fetch it for a single form with [`GET /v1/forms/{id}`](/api-reference/forms/get-by-id). Forms are cached by a sync. Trigger an on-demand refresh with [`POST /v1/forms/fetch`](/api-reference/forms/fetch); a daily scheduler also keeps the cache current. ### When to use this Use the list to find the form you want to document an encounter with. The `emr_form_id` of the chosen form is the `form_id` you pass to [`POST /v1/encounters`](/api-reference/encounters/create). Then read that form's definition to get the question ids for the encounter `answers`. ### Example Request ```bash theme={null} curl https://api.usecobalt.com/v1/forms \ -H 'client_id: ci_live_198908HJDKJSH98789OHKJL' \ -H 'client_secret: cs_live_9827hofdsklOYYHJLJh' \ -H 'access_token: 493JKLHIU98789hLKH9HHJH' ``` ### Example Response ```json theme={null} { "success": true, "forms": [ { "id": "123e4567-e89b-12d3-a456-426614174000", "emr_form_id": "392", "emr_fvid": "1045", "name": "GAD-7", "type": "Assessment", "version": "3", "status": "Active", "build_date": "2026-01-15", "synced_at": "2026-08-26T09:00:00.000Z", "created_at": "2026-01-20T12:00:00.000Z", "updated_at": "2026-08-26T09:00:00.000Z" } ] } ``` The fields on each form: * **id**: Cobalt form id (UUID). Use this with [`GET /v1/forms/{id}`](/api-reference/forms/get-by-id). * **emr\_form\_id**: The form's id in the EMR. This is the `form_id` you pass to [`POST /v1/encounters`](/api-reference/encounters/create). * **emr\_fvid**: The EMR form-version id, or `null`. * **name**: Form name. * **type**, **version**, **status**, **build\_date**: Form metadata from the EMR (any may be `null`). * **synced\_at**: When the form was last synced into the cache. # Get Form Source: https://docs.usecobalt.com/api-reference/forms/get-by-id GET /forms/{id} Returns one cached form with its full definition. Returns a single cached form by its Cobalt id, including the full `definition` — the nested tree of categories, questions, and selectable answers. Use this to discover the question ids you supply in the `answers` object of [`POST /v1/encounters`](/api-reference/encounters/create). The `id` path parameter is the Cobalt form id (UUID) from [`GET /v1/forms`](/api-reference/forms/get). ### The definition structure `definition.categories` is an ordered tree. A category holds child `categories` and `questions`; a question holds selectable `answers`. * Each **question** carries an `emrQuestionId`. That id is the key you use in the encounter `answers` object. * Each **answer** carries a `value` — the option a question can be scored/set to (for a scored questionnaire this is the numeric score, e.g. `"0"`..`"3"`). * `isRequired` / `isFormRequired` tell you which questions must be answered; omitting a required answer fails the encounter with `Missing required answers`. The form metadata fields (`emr_form_id`, `synced_at`, ...) are snake\_case. The nested `definition` is passed through as the EMR structures it and keeps its own field names (e.g. `emrQuestionId`, `emrCategoryId`). Treat the definition as an opaque tree — read the ids and values out of it rather than depending on its exact field casing. ### Example Request ```bash theme={null} curl https://api.usecobalt.com/v1/forms/123e4567-e89b-12d3-a456-426614174000 \ -H 'client_id: ci_live_198908HJDKJSH98789OHKJL' \ -H 'client_secret: cs_live_9827hofdsklOYYHJLJh' \ -H 'access_token: 493JKLHIU98789hLKH9HHJH' ``` ### Example Response ```json theme={null} { "success": true, "form": { "id": "123e4567-e89b-12d3-a456-426614174000", "emr_form_id": "392", "emr_fvid": "1045", "name": "GAD-7", "type": "Assessment", "version": "3", "status": "Active", "build_date": "2026-01-15", "synced_at": "2026-08-26T09:00:00.000Z", "created_at": "2026-01-20T12:00:00.000Z", "updated_at": "2026-08-26T09:00:00.000Z", "definition": { "categories": [ { "emrCategoryId": "7781", "parentEmrCategoryId": null, "name": "GAD-7", "order": 1, "categories": [], "questions": [ { "emrQuestionId": "453149", "emrCategoryId": "7781", "order": 1, "text": "Feeling nervous, anxious, or on edge", "formatCode": "RB", "formatLabel": "Radio Button", "isRequired": true, "isFormRequired": false, "externalId": null, "snomedCode": null, "loincCode": null, "units": null, "calcFormula": null, "answers": [ { "emrAnswerId": "980011", "value": "0", "order": 1 }, { "emrAnswerId": "980012", "value": "1", "order": 2 }, { "emrAnswerId": "980013", "value": "2", "order": 3 }, { "emrAnswerId": "980014", "value": "3", "order": 4 } ] } ] } ] } } } ``` ### Error Responses #### Invalid ID Format ```json theme={null} { "success": false, "message": "Invalid form ID format. Must be a valid UUID." } ``` #### Form Not Found ```json theme={null} { "success": false, "message": "Form not found" } ``` # Get Insurance Providers Source: https://docs.usecobalt.com/api-reference/insurance-providers/get GET /insurance-providers Retrieve insurance providers from the EMR system. ## Use Cases * Get the correct `insurance_provider_id` before adding patient insurance * Search providers by name for autocomplete functionality * Display paginated lists of insurance options to end users * Filter providers by active status ## Query Parameters * **name** (string, optional): Filter by provider name (case-insensitive partial match) * **page** (integer, optional): Page number (default: 1, min: 1) * **page\_size** (integer, optional): Items per page (default: 100, max: 100) * **active** (boolean, optional): Filter by active status (true/false) * **ehr\_id** (string): Filter by EMR system's identifier. ## Example Requests ### Get First Page ```bash theme={null} curl -X GET "https://api.usecobalt.com/v1/insurance-providers" \ -H "client_id: your_client_id" \ -H "client_secret: your_client_secret" \ -H "access_token: your_access_token" ``` ### Search by Name ```bash theme={null} curl -X GET "https://api.usecobalt.com/v1/insurance-providers?name=blue%20cross" \ -H "client_id: your_client_id" \ -H "client_secret: your_client_secret" \ -H "access_token: your_access_token" ``` ### Get Page 2 with 50 Items ```bash theme={null} curl -X GET "https://api.usecobalt.com/v1/insurance-providers?page=2&page_size=50" \ -H "client_id: your_client_id" \ -H "client_secret: your_client_secret" \ -H "access_token: your_access_token" ``` ### Filter Active Providers ```bash theme={null} curl -X GET "https://api.usecobalt.com/v1/insurance-providers?active=true" \ -H "client_id: your_client_id" \ -H "client_secret: your_client_secret" \ -H "access_token: your_access_token" ``` ## Example Response ```json theme={null} { "success": true, "insurance_providers": [ { "id": "abc123def456", "name": "AHMC Health", "payer_id": "98999", "active": true, "created_at": "2025-01-01T10:00:00.000Z" }, { "id": "xyz789ghi012", "name": "Blue Cross Blue Shield", "payer_id": "12345", "active": true, "created_at": "2025-01-01T10:00:00.000Z" } ], "pagination": { "current_page": 1, "total_pages": 5, "total_count": 450, "page_size": 100 } } ``` ## Response Fields ### Top-Level Fields * **success** (boolean): Whether the request was successful * **insurance\_providers** (array): Array of insurance provider objects * **pagination** (object): Pagination metadata ### Insurance Provider Object * **id** (string): Cobalt insurance provider ID - use this value in `POST /v1/patients/:patient_mrn/insurances` * **name** (string): Insurance provider name as it appears in the EMR system * **payer\_id** (string): Insurance payer ID * **active** (boolean): Whether the provider is currently active * **created\_at** (string): ISO 8601 timestamp of when the provider was added ### Pagination Object * **current\_page** (integer): Current page number * **total\_pages** (integer): Total number of pages * **total\_count** (integer): Total number of providers matching the filter * **page\_size** (integer): Number of items per page When adding patient insurance, using the `insurance_provider_id` from this endpoint is the most reliable method for identifying the insurance provider. # Introduction Source: https://docs.usecobalt.com/api-reference/introduction Cobalt API ## Agent-ready API context Copy these resources into your coding agent to give it the API contract and Cobalt-specific EHR guidance. ```text Copy for your agent theme={null} Use these Cobalt API resources: OpenAPI specification: https://docs.usecobalt.com/api-reference/openapi.json Agent guide: https://docs.usecobalt.com/llms.txt ``` The machine-readable API contract for every endpoint, with per-EMR support tags. An index of the concepts and endpoints, written for agents. Append `.md` to any docs page URL for its raw markdown. **Per-EMR support lives in the spec, not in these pages.** An operation's `x-supported-emrs` lists the EMRs that support that endpoint. A request field's `x-required-for-emrs` lists the EMRs that require it, and `x-allowed-values-for-emrs` maps each EMR to the values it accepts. Consult those tags rather than assuming support. You need a client\_id and client\_secret to interact with the Cobalt API. You will also need an access\_token for each user which can be obtained in through the Cobalt Link flow. ## Authentication Headers | Parameter | Type | Description | | - | - | - | | client\_id | string | Your Cobalt API client id | | client\_secret | string | Your Cobalt API secret | | access\_token | string | The access token from the Link Flow | Example request: ```bash theme={null} curl -X GET https://api.usecobalt.com/v1/appointments \ -H 'Content-Type: application/json' \ -H 'client_id: "%COBALT_CLIENT_ID%"' \ -H 'client_secret: "%COBALT_CLIENT_SECRET%"' \ -H 'access_token: "%USER_ACCESS_TOKEN%"' \ -d '{ ... }' ``` ## Response Warnings Some responses include a top-level `warnings` array of human-readable strings. Warnings are non-fatal: the request still succeeds and the data is returned as usual. They flag conditions worth your attention without failing the call. Unrecognized query parameters are one such condition. If a `GET` request includes a query parameter the endpoint does not support (a typo, or a parameter that was renamed or removed), it is **ignored, not applied**, and the response includes a warning that names it, so the mistake is visible rather than silent. The same message is also returned in a `Cobalt-Warning` response header. ```json theme={null} { "success": true, "patients": [ ... ], "warnings": [ "Ignored unrecognized query parameter: include_inactve. Unrecognized parameters are ignored, not applied. Check the names against the API reference; this may indicate a typo or a renamed or removed parameter." ] } ``` Treat a warning as a signal to check your request against this reference. Unknown parameters are never rejected with an error, so an integration that relies on one silently having no effect will keep working, but the warning tells you it is doing nothing. # Create Link Token Source: https://docs.usecobalt.com/api-reference/link/create-token POST /link/token/create Create a Link token to initialize the Link flow. Creates a Link token that you'll use to initialize the Link flow in your frontend application. ### Request Body Parameters #### Required Parameters * `user_id` (string) - A unique identifier for the end user from your system. Do not use PII like email or phone. * `org_id` (string) - A unique identifier for the organization from your system (e.g., clinic or hospital ID). #### Optional Parameters (for creating new organizations) If the `org_id` doesn't exist yet, provide these parameters to create the organization: * `emr_name` (string) - The EMR system name. Contact support for a list of supported EMR systems. * `emr_instance_domain` (string) - The EMR instance URL (e.g., `https://your-emr-instance.com`) #### Other Optional Parameters * `access_token` (string) - Include to use [update mode](/docs/link/update-mode) for an existing connection ### Example Request (Existing Organization) ```bash theme={null} curl -X POST https://api.usecobalt.com/link/token/create \ -H 'Content-Type: application/json' \ -H 'client_id: ci_live_198908HJDKJSH98789OHKJL' \ -H 'client_secret: cs_live_9827hofdsklOYYHJLJh' \ -d '{ "user_id": "user_12345", "org_id": "org_67890" }' ``` ### Example Request (New Organization) ```bash theme={null} curl -X POST https://api.usecobalt.com/link/token/create \ -H 'Content-Type: application/json' \ -H 'client_id: ci_live_198908HJDKJSH98789OHKJL' \ -H 'client_secret: cs_live_9827hofdsklOYYHJLJh' \ -d '{ "user_id": "user_12345", "org_id": "org_67890", "emr_name": "YourEMRName", "emr_instance_domain": "https://your-emr-instance.com" }' ``` ### Example Response ```json theme={null} { "success": true, "token": "lt_198908HJDKJSH98789OHKJL" } ``` ### Update Mode To use update mode for an existing connection, include the `access_token` in your request: ```bash theme={null} curl -X POST https://api.usecobalt.com/link/token/create \ -H 'Content-Type: application/json' \ -H 'client_id: ci_live_198908HJDKJSH98789OHKJL' \ -H 'client_secret: cs_live_9827hofdsklOYYHJLJh' \ -d '{ "user_id": "user_12345", "org_id": "org_67890", "access_token": "493JKLHIU98789hLKH9HHJH" }' ``` The Link token is single-use and expires after 30 minutes. # Exchange Public Token Source: https://docs.usecobalt.com/api-reference/link/exchange-token GET /link/token/exchange Exchange a public token for an access token. After a user successfully completes the Link flow, you'll receive a `public_token`. Exchange this for an `access_token` that you can use for subsequent API calls. ### Example Request ```bash theme={null} curl -X GET https://api.usecobalt.com/v1/link/token/exchange \ -H 'Content-Type: application/json' \ -H 'client_id: ci_live_198908HJDKJSH98789OHKJL' \ -H 'client_secret: cs_live_9827hofdsklOYYHJLJh' \ -G \ --data-urlencode "public_token=pt_198908HJDKJSH98789OHKJL" ``` ### Example Response ```json theme={null} { "success": true, "token": "493JKLHIU98789hLKH9HHJH" } ``` Store this access token securely - you'll need it for all future API calls related to this user's EHR connection. # Get Locations Source: https://docs.usecobalt.com/api-reference/locations/get GET /locations Returns a list of locations associated with an account. ### Understanding Location IDs Each location has two identifiers: * **`id`**: Cobalt's internal identifier (32-character UUID without hyphens) * Use when updating location settings like status * Operations: `PATCH /v1/locations/{id}` * **`ehr_id`**: Your EMR system's location identifier * Use when creating appointments or other EMR operations * Operations: `POST /v1/appointments` (`location` field) **Why two IDs?** Different operations work in different contexts. Location management (updating status) modifies Cobalt's cached configuration, while appointment creation communicates directly with your EMR. Cobalt uses its own IDs to manage location settings independently of EMR constraints, then maps to EMR IDs when interacting with your EMR system. **Quick Reference:** * Updating location settings → Use `id` * Creating appointments → Use `ehr_id` ### Example Request ```bash theme={null} curl -X GET https://api.usecobalt.com/v1/locations \ -H 'Content-Type: application/json' \ -H 'client_id: ci_live_198908HJDKJSH98789OHKJL' \ -H 'client_secret: cs_live_9827hofdsklOYYHJLJh' \ -H 'access_token: 493JKLHIU98789hLKH9HHJH' ``` ### Example Response ```json theme={null} { "success": true, "locations": [ { "id": "abc123def4567890abcdef1234567890", "ehr_id": "2452", "name": "Happy Clinic", "address": { "address1": "1234 Main St", "address2": "", "city": "Sunnyland", "state": "CA", "zip": "23423" }, "phone": "2223334444", "fax": "5556667777", "status": "active" } ] } ``` # Update Location Status Source: https://docs.usecobalt.com/api-reference/locations/update PATCH /locations/{id} Updates the status of a location to active or inactive. **Use the Cobalt `id` in the URL path.** The `{id}` parameter should be the Cobalt location ID (the `id` field from GET responses), not the `ehr_id`. ### Example Request ```bash theme={null} curl -X PATCH https://api.usecobalt.com/v1/locations/abc123def4567890abcdef1234567890 \ -H 'Content-Type: application/json' \ -H 'client_id: ci_live_198908HJDKJSH98789OHKJL' \ -H 'client_secret: cs_live_9827hofdsklOYYHJLJh' \ -H 'access_token: 493JKLHIU98789hLKH9HHJH' \ -d '{ "status": "inactive" }' ``` ### Example Response ```json theme={null} { "success": true, "message": "Location status updated successfully.", "location_id": "abc123def4567890abcdef1234567890" } ``` ### Notes * The status update is case-insensitive ("active", "Active", "ACTIVE" are all accepted) * Only the `status` field can be updated through this endpoint * The location must belong to your organization * Invalid location IDs will return a 404 error # Send Message Source: https://docs.usecobalt.com/api-reference/messages/create POST /messages Sends a message to the specified provider. ### Example Request ```bash theme={null} curl -X POST https://api.usecobalt.com/v1/messages \ -H 'Content-Type: application/json' \ -H 'client_id: ci_live_198908HJDKJSH98789OHKJL' \ -H 'client_secret: cs_live_9827hofdsklOYYHJLJh' \ -H 'access_token: 493JKLHIU98789hLKH9HHJH' \ -d '{ "provider": "123456789", "subject": "Test Message", "message": "This is a test message content." }' ``` ### Example Response ```json theme={null} { "success": true, "message": "Message processing. A webhook event will be sent upon completion.", "message_id": "msg_123456789", "job_id": 15550942 } ``` ### Request Parameters The `/messages` endpoint requires the following fields: * **provider** (required): The EHR provider ID to send the message to * **subject** (required): The subject line for the message * **message** (required): The content of the message to be sent # Create Note Source: https://docs.usecobalt.com/api-reference/notes/create POST /notes Creates a note for a given appointment. **Use the Cobalt `appointment_id`.** The `appointment_id` parameter should be the Cobalt appointment ID returned from API responses or GET endpoints, not the EHR appointment ID. The `note` parameter accepts a structured object with separate fields for clinical documentation, along with optional ICD-10 and CPT codes. The `note` keys depend on the connected EHR: use the HPI format (`hpi`, `exam`, `assessment`, `treatment`) or the SOAP format (`subjective`, `objective`, `assessment`, `plan`), as the examples below show. ### Example Request (HPI format) ```bash theme={null} # eClinicalWorks curl -X POST https://api.usecobalt.com/v1/notes \ -H 'Content-Type: application/json' \ -H 'client_id: ci_live_198908HJDKJSH98789OHKJL' \ -H 'client_secret: cs_live_9827hofdsklOYYHJLJh' \ -H 'access_token: 493JKLHIU98789hLKH9HHJH' \ -d '{ "appointment_id": "728948", "note": { "hpi": "Patient presents with persistent lower back pain for 2 weeks. Pain is described as dull and achy, rated 6/10. Worse with prolonged sitting and bending forward. Denies radiation to legs, numbness, or tingling. No bowel or bladder changes. Patient tried over-the-counter ibuprofen with minimal relief.", "exam": "Vital signs: BP 128/82, HR 74, Temp 98.6°F. General: Alert and oriented x3, in no acute distress. MSK: Limited range of motion in lumbar spine with forward flexion. Tenderness to palpation over L4-L5 region. Negative straight leg raise bilaterally. Motor strength 5/5 in bilateral lower extremities. Sensation intact.", "assessment": "Acute mechanical low back pain, likely lumbar strain. No red flags for serious pathology.", "treatment": "Prescribed naproxen 500mg BID for 10 days. Recommended ice/heat therapy alternating every 20 minutes. Physical therapy referral placed for core strengthening and flexibility exercises. Patient educated on proper lifting mechanics and posture. Return precautions discussed including progressive neurological symptoms." }, "icd_10_codes": ["M54.5", "M54.50"], "cpt_codes": ["99213", "97110"] }' ``` ### Example Request (SOAP format) ```bash theme={null} # Practice Fusion (cpt_codes are not written to the EHR) curl -X POST https://api.usecobalt.com/v1/notes \ -H 'Content-Type: application/json' \ -H 'client_id: ci_live_198908HJDKJSH98789OHKJL' \ -H 'client_secret: cs_live_9827hofdsklOYYHJLJh' \ -H 'access_token: 493JKLHIU98789hLKH9HHJH' \ -d '{ "appointment_id": "728948", "note": { "subjective": "Patient presents with persistent lower back pain for 2 weeks. Pain is described as dull and achy, rated 6/10. Worse with prolonged sitting and bending forward. Denies radiation to legs, numbness, or tingling.", "objective": "Vital signs: BP 128/82, HR 74, Temp 98.6°F. Limited range of motion in lumbar spine with forward flexion. Tenderness to palpation over L4-L5 region. Negative straight leg raise bilaterally.", "assessment": "Acute mechanical low back pain, likely lumbar strain. No red flags for serious pathology.", "plan": "Prescribed naproxen 500mg BID for 10 days. Physical therapy referral placed for core strengthening. Return precautions discussed." }, "icd_10_codes": ["M54.5", "M54.50"] }' ``` ### Example Response ```json theme={null} { "success": true, "message": "Note upload in progress. A webhook event will be sent upon completion.", "job_id": 12345 } ``` ### Webhook Notifications When the note processing is complete, we will send a webhook to your registered endpoint. Here are examples of what those webhook payloads will look like: See the [Webhook Events reference](/docs/webhooks/operation-events) for the full field breakdown and error codes. #### Success ```json theme={null} { "id": "evt_1J9X2q2eZvKYlo2CluR9g9gV", "access_token_reference_id": "user_1J9X2q2eZvKYlo2Cxyz", "object": "event", "created": "2023-10-27T10:30:00Z", "type": "note.uploaded", "job_id": 12345, "data": { "appointment_id": "728948", "mrn": "12345", "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": [] } } } ``` #### Failure ```json theme={null} { "id": "evt_1J9X2q2eZvKYlo2CluR9g9gW", "access_token_reference_id": "user_1J9X2q2eZvKYlo2Cxyz", "object": "event", "created": "2023-10-27T10:35:00Z", "type": "note.failed", "job_id": 12345, "data": { "appointment_id": "728948", "mrn": "12345", "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." } ] } } ``` # Search Order Catalog Source: https://docs.usecobalt.com/api-reference/orders/catalog POST /orders/catalog/fetch Searches the practice's orderable lab, imaging, or procedure catalog. Catalog searches run asynchronously against the live EMR: a valid request returns immediately with a `202`, and an `order_catalog.searched` webhook is sent with the results when the search completes. Use the `item_id` values from the results when creating an order via `POST /v1/orders`. ### Request Parameters #### Required Fields * **type** (string, required): Which catalog to search. One of `lab`, `imaging`, or `procedure`. #### Optional Fields * **query** (string, optional): Name filter (prefix or substring). Omit to page the full catalog. * **page** (integer, optional): Page number (1-based). Defaults to `1`. * **page\_size** (integer, optional): Results per page. Defaults to `25`. * **callback\_urls** (string\[], optional): URLs to receive the results webhook, in addition to the account webhook. ### Example Request ```bash theme={null} curl -X POST https://api.usecobalt.com/v1/orders/catalog/fetch \ -H 'Content-Type: application/json' \ -H 'client_id: ci_live_198908HJDKJSH98789OHKJL' \ -H 'client_secret: cs_live_9827hofdsklOYYHJLJh' \ -H 'access_token: 493JKLHIU98789hLKH9HHJH' \ -d '{ "type": "lab", "query": "glucose", "page": 1, "page_size": 25 }' ``` ### Example Response ```json theme={null} { "success": true, "status": "queued", "message": "Catalog search started. A order_catalog.searched webhook will be sent when it completes.", "job_id": 12345 } ``` ### Webhook Notifications When the search completes, we send an `order_catalog.searched` webhook to your registered endpoint: ```json theme={null} { "id": "evt_1J9X2q2eZvKYlo2CluR9g9gV", "access_token_reference_id": "user_1J9X2q2eZvKYlo2Cxyz", "object": "event", "created": "2025-10-09T10:30:00Z", "type": "order_catalog.searched", "job_id": "12345", "data": { "type": "lab", "page": 1, "has_more": true, "items": [ { "item_id": "80048", "name": "Comprehensive Metabolic Panel", "billable": true }, { "item_id": "82947", "name": "Glucose, Serum", "billable": true } ] } } ``` # Create Order Source: https://docs.usecobalt.com/api-reference/orders/create POST /orders Creates a lab, imaging, or procedure order in the provider's EMR system. Orders are created asynchronously: a valid request returns immediately with a `202`, and an `order.created` webhook is sent when processing completes. If creation fails terminally, an `order.create_failed` webhook is sent instead. Identify the test or procedure to order with either `item_id` or `name`. Discover valid values via [Search Order Catalog](/api-reference/orders/catalog). When both are provided, `item_id` wins. ### Request Parameters #### Required Fields * **type** (string, required): Order type. One of `lab`, `imaging`, or `procedure`. * **patient\_mrn** (string, required): Medical Record Number of the patient the order is for. One of the following is also required: * **item\_id** (string): EMR catalog id of the test/procedure to order. Discover ids via `POST /v1/orders/catalog/fetch`. * **name** (string): Catalog name of the test/procedure, resolved against the catalog when `item_id` is omitted. #### Optional Fields * **provider\_id** (string, optional): EMR id of the ordering provider. * **facility\_id** (string, optional): EMR id of the ordering facility/location. * **assigned\_to\_id** (string, optional): EMR staff id the order is assigned to. * **order\_date** (string, optional): Order date (ISO 8601, YYYY-MM-DD). * **collected\_date** (string, optional): Specimen collection date (ISO 8601, YYYY-MM-DD). Labs. * **collection\_time** (string, optional): Specimen collection time, e.g. `"12:58 PM"`. * **performed\_date** (string, optional): Performed date (ISO 8601, YYYY-MM-DD). Diagnostic imaging. * **high\_priority** (boolean, optional): Flag the order high priority. * **notes** (string, optional): Order notes. * **clinical\_info** (string, optional): Clinical information for the order. * **callback\_urls** (string\[], optional): URLs to receive the `order.created` webhook, in addition to the account webhook. ### Example Requests ```bash theme={null} curl -X POST https://api.usecobalt.com/v1/orders \ -H 'Content-Type: application/json' \ -H 'client_id: ci_live_198908HJDKJSH98789OHKJL' \ -H 'client_secret: cs_live_9827hofdsklOYYHJLJh' \ -H 'access_token: 493JKLHIU98789hLKH9HHJH' \ -d '{ "type": "lab", "patient_mrn": "12345", "item_id": "80048", "provider_id": "789", "facility_id": "25", "collected_date": "2026-03-15", "collection_time": "12:58 PM" }' ``` ```bash theme={null} curl -X POST https://api.usecobalt.com/v1/orders \ -H 'Content-Type: application/json' \ -H 'client_id: ci_live_198908HJDKJSH98789OHKJL' \ -H 'client_secret: cs_live_9827hofdsklOYYHJLJh' \ -H 'access_token: 493JKLHIU98789hLKH9HHJH' \ -d '{ "type": "imaging", "patient_mrn": "12345", "name": "Chest X-Ray", "provider_id": "789", "performed_date": "2026-03-15", "high_priority": true, "clinical_info": "Persistent cough, rule out pneumonia." }' ``` ### Example Response ```json theme={null} { "success": true, "status": "queued", "message": "Order queued for creation. An order.created webhook will be sent when it completes.", "job_id": 12345 } ``` ### Error Responses #### Missing Required Field ```json theme={null} { "success": false, "message": "Missing required field: patient_mrn" } ``` #### Missing Order Identifier ```json theme={null} { "success": false, "message": "Provide either item_id or name to identify the order." } ``` ### Webhook Notifications When order processing completes, we send a webhook to your registered endpoint. #### Success ```json theme={null} { "id": "evt_1J9X2q2eZvKYlo2CluR9g9gV", "access_token_reference_id": "user_1J9X2q2eZvKYlo2Cxyz", "object": "event", "created": "2025-10-09T10:30:00Z", "type": "order.created", "job_id": "12345", "data": { "emr_order_id": "998877", "emr_encounter_id": "445566", "patient_mrn": "12345" } } ``` #### Failure ```json theme={null} { "id": "evt_1J9X2q2eZvKYlo2CluR9g9gW", "access_token_reference_id": "user_1J9X2q2eZvKYlo2Cxyz", "object": "event", "created": "2025-10-09T10:35:00Z", "type": "order.create_failed", "job_id": "12345", "data": { "patient_mrn": "12345", "failure_reason": "Order create failed after multiple attempts. Please try again later or contact support." } } ``` # Get Orders Source: https://docs.usecobalt.com/api-reference/orders/get GET /orders Returns a list of orders for a patient. ### Example Request ```bash theme={null} curl -X GET https://api.usecobalt.com/v1/orders \ -H 'Content-Type: application/json' \ -H 'client_id: ci_live_198908HJDKJSH98789OHKJL' \ -H 'client_secret: cs_live_9827hofdsklOYYHJLJh' \ -H 'access_token: 493JKLHIU98789hLKH9HHJH' \ -G \ --data-urlencode "patient_mrn=414421" \ --data-urlencode "start_date=2024-01-01" \ --data-urlencode "end_date=2024-03-31" ``` ### Example Response ```json theme={null} { "success": true, "orders": [ { "id": "ord_123456", "order_id": "998877", "order_name": "Comprehensive Metabolic Panel", "order_date": "2024-02-15", "provider_id": "prov_789", "patient_mrn": "414421", "patient_name": "P-778899", "patient_dob": "1980-06-15", "appointment_id": "appt_456", "priority": "routine", "assigned_to_name": "Jane Doe", "emr_assigned_to_id": "staff_101", "lab_results": null }, { "id": "ord_789012", "order_id": "998912", "order_name": "Chest X-Ray", "order_date": "2024-03-01", "provider_id": "prov_789", "patient_mrn": "414421", "patient_name": "P-778899", "patient_dob": "1980-06-15", "appointment_id": "appt_789", "priority": null, "assigned_to_name": null, "emr_assigned_to_id": null, "lab_results": null } ] } ``` # Get Order Source: https://docs.usecobalt.com/api-reference/orders/get-by-id GET /orders/{id} Returns a synced order and its writable result components. Returns a single synced order along with its `result_components` — the writable result columns for that order. Each component's `field_id` is the write target accepted by [Add Order Results](/api-reference/orders/results), so fetch the order first to learn which components you can populate. ### Example Request ```bash theme={null} curl -X GET https://api.usecobalt.com/v1/orders/ord_123456 \ -H 'Content-Type: application/json' \ -H 'client_id: ci_live_198908HJDKJSH98789OHKJL' \ -H 'client_secret: cs_live_9827hofdsklOYYHJLJh' \ -H 'access_token: 493JKLHIU98789hLKH9HHJH' ``` ### Example Response ```json theme={null} { "success": true, "order": { "id": "ord_123456", "order_id": "998877", "appointment_id": "445566", "patient_name": "P-778899", "patient_mrn": "12345", "order_date": "2026-02-15", "order_name": "1hr Glucose Tolerance (OB)", "provider_id": "789", "patient_dob": "1980-06-15", "lab_results": null, "priority": "routine", "assigned_to_name": "Jane Doe", "emr_assigned_to_id": "staff_101", "result_components": [ { "field_id": "col_1001", "name": "1hr Glucose (OB)" }, { "field_id": "col_1002", "name": "Fasting Glucose (OB)" } ] } } ``` # Create Order Results Source: https://docs.usecobalt.com/api-reference/orders/results POST /orders/{id}/results Creates a lab-result record for an order and queues it for the EHR. Creating results is asynchronous: a valid request creates a lab-result record, returns immediately with the `lab_result_id`, and queues the record for the EHR. A `lab_result.created` webhook is sent when processing completes. If the operation fails terminally, a `lab_result.create_failed` webhook is sent instead. Each result targets a component of the order. Reference a component by its `component` (name) or `field_id`, both of which come from the `result_components` returned by [Get Order](/api-reference/orders/get-by-id). ### Request Parameters #### Required Fields * **results** (array, required): Result values, one per component. At least one entry is required. Each entry is an object with: * **value** (string or number, required): The result value. * **component** (string): The component name, from `GET /v1/orders/{id}`. * **field\_id** (string): The component's EMR id, from `GET /v1/orders/{id}`. Provide `component` or `field_id` to identify each component. #### Optional Fields * **result\_date** (string, optional): Result date (ISO 8601, YYYY-MM-DD). Defaults to today when omitted. * **collected\_date** (string, optional): Specimen collection date (ISO 8601, YYYY-MM-DD). * **collection\_time** (string, optional): Specimen collection time, e.g. `"12:58 PM"`. * **callback\_urls** (string\[], optional): URLs to receive the `lab_result.created` webhook, in addition to the account webhook. ### Example Request ```bash theme={null} curl -X POST https://api.usecobalt.com/v1/orders/ord_123456/results \ -H 'Content-Type: application/json' \ -H 'client_id: ci_live_198908HJDKJSH98789OHKJL' \ -H 'client_secret: cs_live_9827hofdsklOYYHJLJh' \ -H 'access_token: 493JKLHIU98789hLKH9HHJH' \ -d '{ "results": [ { "component": "1hr Glucose (OB)", "value": 142 }, { "field_id": "col_1002", "value": "88" } ], "result_date": "2026-03-17", "collected_date": "2026-03-16", "collection_time": "09:15 AM" }' ``` ### Example Response ```json theme={null} { "success": true, "message": "Lab result created and queued. A lab_result.created webhook will be sent when it completes.", "lab_result_id": "lr_123456", "order_id": "ord_123456", "job_id": 12345 } ``` ### Webhook Notifications When processing completes, we send a webhook to your registered endpoint. #### Success ```json theme={null} { "id": "evt_1J9X2q2eZvKYlo2CluR9g9gV", "access_token_reference_id": "user_1J9X2q2eZvKYlo2Cxyz", "object": "event", "created": "2025-10-09T10:30:00Z", "type": "lab_result.created", "job_id": "12345", "data": { "lab_result_id": "lr_123456", "order_id": "ord_123456", "emr_order_id": "998877", "emr_encounter_id": "445566", "patient_mrn": "12345" } } ``` #### Failure ```json theme={null} { "id": "evt_1J9X2q2eZvKYlo2CluR9g9gW", "access_token_reference_id": "user_1J9X2q2eZvKYlo2Cxyz", "object": "event", "created": "2025-10-09T10:35:00Z", "type": "lab_result.create_failed", "job_id": "12345", "data": { "lab_result_id": "lr_123456", "order_id": "ord_123456", "patient_mrn": "12345", "failure_reason": "Lab result creation failed after multiple attempts. Please try again later or contact support." } } ``` # Update Order Source: https://docs.usecobalt.com/api-reference/orders/update PATCH /orders/{id} Updates an existing order's dates, assignee, or priority. Order updates are processed asynchronously: a valid request returns immediately with a `202`, and an `order.updated` webhook is sent when processing completes. If the update fails terminally, an `order.update_failed` webhook is sent instead. ### Request Parameters All fields are optional; send only the fields you want to change. * **result\_date** (string, optional): Result date (ISO 8601, YYYY-MM-DD). * **collected\_date** (string, optional): Specimen collection date (ISO 8601, YYYY-MM-DD). * **collection\_time** (string, optional): Specimen collection time, e.g. `"12:58 PM"`. * **performed\_date** (string, optional): Performed date (ISO 8601, YYYY-MM-DD). Diagnostic imaging. * **assigned\_to\_id** (string, optional): EMR staff id to assign the order to. * **high\_priority** (boolean, optional): Flag the order high priority. * **callback\_urls** (string\[], optional): URLs to receive the `order.updated` webhook, in addition to the account webhook. ### Example Request ```bash theme={null} curl -X PATCH https://api.usecobalt.com/v1/orders/ord_123456 \ -H 'Content-Type: application/json' \ -H 'client_id: ci_live_198908HJDKJSH98789OHKJL' \ -H 'client_secret: cs_live_9827hofdsklOYYHJLJh' \ -H 'access_token: 493JKLHIU98789hLKH9HHJH' \ -d '{ "collected_date": "2026-03-16", "collection_time": "09:15 AM", "assigned_to_id": "staff_101", "high_priority": true }' ``` ### Example Response ```json theme={null} { "success": true, "status": "queued", "message": "Order update queued. An order.updated webhook will be sent when it completes.", "job_id": 12345 } ``` ### Webhook Notifications When processing completes, we send a webhook to your registered endpoint. #### Success ```json theme={null} { "id": "evt_1J9X2q2eZvKYlo2CluR9g9gV", "access_token_reference_id": "user_1J9X2q2eZvKYlo2Cxyz", "object": "event", "created": "2025-10-09T10:30:00Z", "type": "order.updated", "job_id": "12345", "data": { "emr_order_id": "998877", "emr_encounter_id": "445566", "patient_mrn": "12345" } } ``` #### Failure ```json theme={null} { "id": "evt_1J9X2q2eZvKYlo2CluR9g9gW", "access_token_reference_id": "user_1J9X2q2eZvKYlo2Cxyz", "object": "event", "created": "2025-10-09T10:35:00Z", "type": "order.update_failed", "job_id": "12345", "data": { "patient_mrn": "12345", "failure_reason": "Order update failed after multiple attempts. Please try again later or contact support." } } ``` # Fetch Org Settings Source: https://docs.usecobalt.com/api-reference/org-settings/fetch POST /org-settings/fetch Queues a live sync of your organization's settings from the connected EMR system. Cobalt automatically syncs org settings **nightly for every organization**. You do **not** need to call this endpoint on a schedule — the nightly sync already keeps providers, locations, visit types, and the rest of your org settings up to date. Use this endpoint sparingly, only when you need a refresh sooner than the next nightly run (for example, right after adding a new provider or location in your EMR and wanting it reflected immediately). This is an asynchronous operation. It returns immediately with a `job_execution_id` and delivers an `org_settings.synced` webhook when the sync finishes. To read the synced values, use the corresponding read endpoints such as [Get Providers](/api-reference/providers/get), [Get Locations](/api-reference/locations/get), [Get Visit Types](/api-reference/visit-types/get), [Get Visit Statuses](/api-reference/visit-statuses/get), and [Get Settings](/api-reference/settings/get). ## What Gets Synced A single sync refreshes the full set of organization settings from the EMR: * Providers * Locations * Visit types * Visit statuses * Insurance companies * Pharmacies * Referring providers * Timezone * Required patient fields ## Concurrency Only one org settings sync can be in flight per linked account at a time. If a sync is already running when you call this endpoint, the request returns `409` with the `job_execution_id` of the in-flight sync so you can wait for it to finish rather than queuing a duplicate. ## Example Request ```bash theme={null} curl -X POST https://api.usecobalt.com/v1/org-settings/fetch \ -H "Content-Type: application/json" \ -H "client_id: your_client_id" \ -H "client_secret: your_client_secret" \ -H "access_token: your_access_token" \ -d '{}' ``` ### Routing the completion webhook The completion webhook is delivered to your account's registered webhook URLs by default. To route this specific sync's `org_settings.synced` event elsewhere, include a `callback_urls` array (see [Webhook setup](/docs/webhooks/setup)). Each URL must use HTTPS. ```bash theme={null} curl -X POST https://api.usecobalt.com/v1/org-settings/fetch \ -H "Content-Type: application/json" \ -H "client_id: your_client_id" \ -H "client_secret: your_client_secret" \ -H "access_token: your_access_token" \ -d '{ "callback_urls": ["https://your-domain.com/cobalt-webhook"] }' ``` ### Example Response ```json theme={null} { "success": true, "status": "queued", "message": "Org settings sync started. An org_settings.synced webhook will be sent when it completes.", "job_execution_id": "job_1J9X2q2eZvKYlo2Cmnopqr" } ``` ## Webhook Notification When the sync completes, Cobalt sends an `org_settings.synced` webhook to your registered endpoint (or to `callback_urls` if you supplied them). The payload is intentionally minimal — it signals completion and reports counts of what changed. Re-fetch the synced values from the read endpoints listed above. ```json theme={null} { "id": "508c368e7de13b40f9397eec966d0329", "access_token_reference_id": "your-reference-id", "object": "event", "job_id": "job_1J9X2q2eZvKYlo2Cmnopqr", "created": "2026-07-22T08:50:42.491-07:00", "type": "org_settings.synced", "action": "ehr_sync", "data": { "status": "completed", "summary": { "providers_created": 1, "providers_updated": 3, "locations_created": 0, "locations_updated": 2, "visit_types_created": 0, "visit_types_updated": 5, "visit_statuses_created": 0, "visit_statuses_updated": 0, "insurance_companies_created": 0, "pharmacies_created": 0, "pharmacies_updated": 0, "referring_providers_created": 0, "referring_providers_updated": 1, "policy_types_count": 12, "staff_list_count": 24, "required_patient_fields_synced": true, "timezone_synced": true, "link_org_updated": true } } } ``` You must be subscribed to the `org_settings.synced` event for the webhook to be delivered. See [Webhook setup](/docs/webhooks/setup) to manage your subscriptions. # Create Patient Note Source: https://docs.usecobalt.com/api-reference/patient-notes/create POST /patient-notes Creates a free-standing chart note on a patient. Unlike a note tied to an appointment, this writes directly to the patient's chart. ### Request Parameters * **note** (string, required): Chart note body content * **subject** (string, required): Chart note title * **patient\_mrn** (string, required): Medical Record Number (MRN) of the patient * **location\_id** (string): EMR location ID. Must match a location returned by `GET /v1/locations` for the account. * **tags** (array of strings): One or more note category names, matched against the practice's configured note categories. An unknown category fails the note. ### Example Request ```bash theme={null} curl -X POST https://api.usecobalt.com/v1/patient-notes \ -H 'Content-Type: application/json' \ -H 'client_id: ci_live_198908HJDKJSH98789OHKJL' \ -H 'client_secret: cs_live_9827hofdsklOYYHJLJh' \ -H 'access_token: 493JKLHIU98789hLKH9HHJH' \ -d '{ "note": "Patient reports feeling well since last visit. No new concerns to report.", "subject": "Wellness check-in", "patient_mrn": "MRN-12345", "location_id": "9b409d24-aa13-4ce5-a6b2-ea813eeb9530" }' ``` ### Example Response ```json theme={null} { "success": true, "message": "Patient note processing. A webhook event will be sent upon completion.", "patient_note_id": "abc123def456789012345678", "job_id": 12345 } ``` The response includes: * **patient\_note\_id**: Unique identifier for the created patient note record * **job\_id**: Job execution identifier for tracking the async operation ### Error Responses #### Missing Required Field ```json theme={null} { "success": false, "message": "Missing required field: subject" } ``` #### Invalid Location ```json theme={null} { "success": false, "message": "Invalid location_id. Must be one of: ..." } ``` ### Webhook Notifications When the patient note processing is complete, we will send a webhook to your registered endpoint. #### Success ```json theme={null} { "id": "evt_1J9X2q2eZvKYlo2CluR9g9gV", "access_token_reference_id": "user_1J9X2q2eZvKYlo2Cxyz", "object": "event", "created": "2025-10-09T10:30:00Z", "type": "patient_note.created", "job_id": "12345", "data": { "patient_note_id": "abc123def456789012345678", "emr_note_id": "10d1c8d3-aa69-472a-a9eb-f5fbd12bf99f", "patient_mrn": "MRN-12345", "location_id": "9b409d24-aa13-4ce5-a6b2-ea813eeb9530", "subject": "Wellness check-in", "note": "Patient reports feeling well since last visit. No new concerns to report." } } ``` #### Failure ```json theme={null} { "id": "evt_1J9X2q2eZvKYlo2CluR9g9gW", "access_token_reference_id": "user_1J9X2q2eZvKYlo2Cxyz", "object": "event", "created": "2025-10-09T10:35:00Z", "type": "patient_note.failed", "job_id": "12345", "data": { "patient_note_id": "abc123def456789012345678", "failure_reason": "Failed to create patient note" } } ``` # Create Patient Source: https://docs.usecobalt.com/api-reference/patients/create POST /patients Creates a new patient record. ### Provider IDs for Referrals When creating patients with referral information, use the **`ehr_id`** value from providers: * **`referred_to_provider_id`** (optional): Use the `ehr_id` from `GET /v1/providers` (not the `id`) This value represents the rendering provider identifier your EMR system uses. Cobalt passes it directly to your EMR when creating the patient record. **Don't use the Cobalt `id` here.** The `id` field from GET responses is Cobalt's internal UUID, used only for updating provider settings via PATCH endpoints. ### Referring Provider and PCP Options When creating a patient, you can specify the referring provider and PCP (Primary Care Provider) using either names or provider IDs: #### Option 1: Provider Names (All EMRs) Provide the provider's first and last name: ```json theme={null} { "referring_provider_first_name": "John", "referring_provider_last_name": "Smith", "pcp_first_name": "Jane", "pcp_last_name": "Doe" } ``` #### Option 2: Provider IDs Provide a Cobalt `id` (UUID, with or without dashes). This option: * Automatically looks up the provider's name, phone, and fax * Provides more accurate matching by validating contact information in the EMR * Falls back to using provided names if the ID is not found The accepted source depends on the field: * **`referring_provider_id`**: the `id` from `GET /v1/referring-providers`. * **`pcp_id`**: the `id` from **either** `GET /v1/referring-providers` **or** `GET /v1/providers`. Pass a `/v1/providers` `id` when the patient's PCP is one of the practice's own physicians rather than an external referring provider. Cobalt resolves the UUID against both sets of providers, so you can use whichever endpoint the provider appears in. ```json theme={null} { "referring_provider_id": "e35e82b85df58f08a9b38e06a91e9f1a", "pcp_id": "223ecd1b29e546ca9db6b391eb49b213" } ``` For `pcp_id`, use the `id` field (the Cobalt UUID), not the `ehr_id`. This differs from `referred_to_provider_id`, which expects the `ehr_id` from `GET /v1/providers`. ### Responsible Party By default, the patient is their own responsible party (`responsible_party: "self"`). For minors or patients whose billing is managed by another person, set `responsible_party` to one of the values below and include the guarantor's demographic fields. When a non-self responsible party is provided alongside insurance, Cobalt will: 1. Create a guarantor record in the EMR with the provided demographics 2. Link that guarantor to the insurance record 3. Set the responsible party on the patient ```json theme={null} { "responsible_party": "parent", "responsible_party_first_name": "Jane", "responsible_party_last_name": "Smith", "responsible_party_sex": "female", "responsible_party_dob": "1975-06-15", "responsible_party_phone": "555-555-1234", "responsible_party_email": "jane.smith@example.com", "responsible_party_address_line1": "123 Main St", "responsible_party_address_line2": "Apt 4B", "responsible_party_address_city": "Springfield", "responsible_party_address_state": "IL", "responsible_party_address_zip": "62701", "insurance_name": "Aetna", "insurance_subscriber_number": "ABC123" } ``` **Supported values:** | Value | EMR Code | Description | | - | - | - | | `self` | 1 | Patient is the insured | | `spouse` | 2 | Patient is the spouse of the insured | | `child` | 3 | Natural Child — insured has financial responsibility | | `adopted_child` | 4 | Natural Child — insured does not have financial responsibility (includes legally adopted) | | `step_child` | 5 | Step Child | | `foster_child` | 6 | Foster Child | | `ward_of_court` | 7 | Ward of the Court | | `employee` | 8 | Employee | | `unknown` | 9 | Unknown / Other relationship | | `handicapped_dependent` | 10 | Handicapped Dependent | | `organ_donor` | 11 | Organ Donor | | `cadaver_donor` | 12 | Cadaver Donor | | `grandchild` | 13 | Grandchild | | `niece_nephew` | 14 | Niece/Nephew | | `injured_plaintiff` | 15 | Injured Plaintiff | | `sponsored_dependent` | 16 | Sponsored Dependent | | `minor_dependent` | 17 | Minor Dependent of a Minor Dependent | | `parent` | 18 | Parent | | `grandparent` | 19 | Grandparent | | `life_partner` | 53 | Life Partner | | `other` | 99 | Other | **Optional guarantor fields:** | Field | Type | Description | | - | - | - | | `responsible_party_sex` | string | `male` or `female` | | `responsible_party_dob` | string | Date of birth in `YYYY-MM-DD` format | | `responsible_party_phone` | string | Phone number in `222-333-4444` format | | `responsible_party_email` | string | Email address | | `responsible_party_address_line1` | string | Street address line 1 | | `responsible_party_address_line2` | string | Street address line 2 | | `responsible_party_address_city` | string | City | | `responsible_party_address_state` | string | Two-letter U.S. state abbreviation | | `responsible_party_address_zip` | string | 5-digit ZIP code | `responsible_party_first_name` and `responsible_party_last_name` are required when using a non-self responsible party. The guarantor is created as a separate record in the EMR — if a guarantor with those demographics already exists in your instance, a duplicate will be created. ### Registration Fields Additional demographic and consent fields can be set at patient creation: | Field | Type | Description | | - | - | - | | `language` | string | Preferred language. Must match a valid language name in your EMR instance. | | `race` | string | Race. Must match a valid race name in your EMR instance. | | `ethnicity` | string | Ethnicity. Must match a valid ethnicity code or display name in your EMR instance. | | `release_of_info` | string | Consent for release of information. Accepts `true`/`false` (`true` maps to `Y`, `false` to `N`) or an EMR code: `Y`, `M`, `N`, `I`. Defaults to `Y`. | | `rx_history_consent` | string | Consent for pharmacy history sharing. Accepts `true`/`false` (`true` maps to `Y`, `false` to `N`) or an EMR code: `Y`, `N`, `U`. Defaults to `Y`. | | `self_pay` | boolean | Explicitly marks the patient as self-pay, overriding insurance-derived logic. | | `default_facility_id` | string | The EMR facility ID to set as the patient's primary service location. | | `voice_enabled` | boolean | Enables voice (phone) notifications in the EMR patient communication settings. Defaults to off. | | `text_enabled` | boolean | Enables text (SMS) notifications in the EMR patient communication settings. Defaults to off. | ```json theme={null} { "language": "Spanish", "race": "White", "ethnicity": "Hispanic or Latino", "release_of_info": true, "rx_history_consent": false, "default_facility_id": "25", "voice_enabled": true, "text_enabled": true } ``` **Notification language.** When `voice_enabled` or `text_enabled` is set, the notification language in the EHR follows the patient's `language`: Spanish when `language` contains "spanish" (case-insensitive), otherwise English. These are the only two options exposed for communication notifications. ### Insurance Options When creating a patient, you can specify insurance using either the insurance name or an insurance provider ID: **Option 1: Insurance Name (All EMRs)** ```json theme={null} { "insurance_name": "United Healthcare" } ``` **Option 2: Insurance Provider ID** Use `insurance_provider_id` from `GET /v1/insurance-providers` (UUID without dashes). This option: * Automatically looks up the insurance name, address, city, and state * Provides more accurate matching when there are duplicate insurance names * Falls back to using provided insurance\_name if the ID is not found ```json theme={null} { "insurance_provider_id": "e35e82b85df58f08a9b38e06a91e9f1a" } ``` ### Example Request #### Basic Request ```bash theme={null} curl -X POST https://api.usecobalt.com/v1/patients \ -H 'Content-Type: application/json' \ -H 'client_id: ci_live_198908HJDKJSH98789OHKJL' \ -H 'client_secret: cs_live_9827hofdsklOYYHJLJh' \ -H 'access_token: 493JKLHIU98789hLKH9HHJH' \ -d '{ "last_name": "Smith", "first_name": "John", "phone": "5551234567", "dob": "1980-01-01", "sex": "male" }' ``` #### With Provider Names ```bash theme={null} curl -X POST https://api.usecobalt.com/v1/patients \ -H 'Content-Type: application/json' \ -H 'client_id: ci_live_198908HJDKJSH98789OHKJL' \ -H 'client_secret: cs_live_9827hofdsklOYYHJLJh' \ -H 'access_token: 493JKLHIU98789hLKH9HHJH' \ -d '{ "last_name": "Smith", "first_name": "John", "phone": "5551234567", "dob": "1980-01-01", "sex": "male", "referring_provider_first_name": "Jane", "referring_provider_last_name": "Doe", "pcp_first_name": "Robert", "pcp_last_name": "Johnson" }' ``` #### With Provider IDs ```bash theme={null} curl -X POST https://api.usecobalt.com/v1/patients \ -H 'Content-Type: application/json' \ -H 'client_id: ci_live_198908HJDKJSH98789OHKJL' \ -H 'client_secret: cs_live_9827hofdsklOYYHJLJh' \ -H 'access_token: 493JKLHIU98789hLKH9HHJH' \ -d '{ "last_name": "Smith", "first_name": "John", "phone": "5551234567", "dob": "1980-01-01", "sex": "male", "referring_provider_id": "e35e82b85df58f08a9b38e06a91e9f1a", "pcp_id": "223ecd1b29e546ca9db6b391eb49b213" }' ``` ### Example Response ```json theme={null} { "success": true, "message": "Patient processing. A webhook event will be sent upon completion.", "patient_id": "9988776655443322", "job_id": 12345 } ``` Store the returned `patient_id` to correlate the follow-up webhook. `job_id` is the API log ID for the request and is also present on the resulting webhook, so you can match either identifier back to this call. ### Webhook Notifications When patient processing is complete, we will send a webhook to your registered endpoint. Here are examples of what those webhook payloads will look like: #### Success ```json theme={null} { "id": "evt_pat_succ_g7h8i9", "access_token_reference_id": "user_pat_ref_k3l4m5", "object": "event", "created": "2023-10-29T12:00:00Z", "type": "patient.created", "job_id": "12345", "action": "cobalt_operation", "data": { "patient_id": "9988776655443322", "mrn": "123456", "eligibility_information": [ { "insurance_name": "Aetna", "status": "Active" } ] // returned when check_eligibility=true. Availability depends on EHR support. } } ``` #### Failure Examples The `patient.failed` webhook event includes a `failure_reason` and may contain additional fields in the `data` object depending on the cause of the failure. **Duplicate Patient:** ```json theme={null} { "id": "evt_pat_fail_dup_p2q3r4", "access_token_reference_id": "user_pat_ref_k3l4m5", "object": "event", "created": "2023-10-29T12:05:00Z", "type": "patient.failed", "data": { "patient_id": "9988776655443322", "failure_reason": "Duplicate patient found. Please use the existing patient or update the patient information using the PATCH /v1/patients/:id endpoint.", "existing_mrn": "654321" } } ``` **Referring Provider Not Found:** ```json theme={null} { "id": "evt_pat_fail_ref_s5t6u7", "access_token_reference_id": "user_pat_ref_k3l4m5", "object": "event", "created": "2023-10-29T12:10:00Z", "type": "patient.failed", "data": { "patient_id": "9988776655443323", "failure_reason": "No valid referring provider found. Please update the referring provider using the PATCH /v1/patients/:id endpoint." } } ``` **PCP Not Found:** ```json theme={null} { "id": "evt_pat_fail_pcp_y1z2a3", "access_token_reference_id": "user_pat_ref_k3l4m5", "object": "event", "created": "2023-10-29T12:12:00Z", "type": "patient.failed", "data": { "patient_id": "9988776655443325", "failure_reason": "No valid PCP provider found. Please update the PCP provider using the PATCH /v1/patients/:id endpoint." } } ``` **Insurance Not Found:** ```json theme={null} { "id": "evt_pat_fail_ins_v8w9x0", "access_token_reference_id": "user_pat_ref_k3l4m5", "object": "event", "created": "2023-10-29T12:15:00Z", "type": "patient.failed", "data": { "patient_id": "9988776655443324", "failure_reason": "No valid insurance found. Please update the insurance using the PATCH /v1/patients/:id endpoint." } } ``` Patient creation is asynchronous. Store the returned patient\_id and listen for webhooks to determine the final status. # Fetch Patients Source: https://docs.usecobalt.com/api-reference/patients/fetch POST /patients/fetch Performs a live fetch of patient data from the connected EMR system based on search criteria. This is an async operation that fetches up-to-date patient data directly from the EMR system. If the newest data is not critical, consider using the GET /patients endpoint instead for faster results from cached data. ## Optional Includes Use `include` to opt into additional live-fetch payloads. The API accepts either: * a comma-delimited string, for example `include: "insurances"` * an array of strings, for example `include: ["insurances"]` Every include value: * `insurances` * `problems` * `medical_history` * `labs` * `labs.results` (orders plus discrete result values; implies `labs`) * `referrals` (the patient's incoming, outgoing, and external referrals, each with diagnoses) * `documents` * `encounters` * `encounters.details` (implies `encounters`) * `encounters.appointment_notes` (implies `encounters`) * `encounters.referral` (implies `encounters`) * `encounters.all` (full encounter history instead of the 50 most recent; implies `encounters`) * `episodes` * `notes` * `assessments` * `care_plans` * `clinical_notes` * `medical` * `treatment_plan` * `point_of_care` * `tasks` * `tasks.notes` (tasks plus attached notes; implies `tasks`) * `all_pages` (fetch every page of a name or dob search instead of just the first — see below) Which of these the connected EMR accepts varies. The `include` field in the [OpenAPI spec](https://docs.usecobalt.com/api-reference/openapi.json) carries the accepted set per EMR under `x-allowed-values-for-emrs`. `tasks.notes` implies `tasks` — you do not need to pass both. Requesting `tasks.notes` makes an extra live fetch per task to pull its note history, so only request it when you need the notes. ### Example Request ```bash theme={null} curl -X POST https://api.usecobalt.com/v1/patients/fetch \ -H "Content-Type: application/json" \ -H "client_id: your_client_id" \ -H "client_secret: your_client_secret" \ -H "access_token: your_access_token" \ -d '{ "mrn": "MRN123456", "search_by": "mrn", "include": ["insurances", "problems", "medical_history", "labs.results", "referrals"] }' ``` ## Narrowing a Search A `name` or `dob` search returns a single page of matches. The page size is set by the EMR and can be as low as 20, so a common name, or a busy date of birth, can easily exceed one page. Add **one** secondary filter to narrow the results at the source: * Narrow a **name** search with `dob` (YYYY-MM-DD) or `phone` (222-333-4444). * Narrow a **dob** search with a name (`first_name` / `last_name`). Only one secondary filter is allowed. The filter is applied by the EMR (it intersects both criteria server-side), so it reduces the result set rather than trimming it after the fact. A name plus a date of birth resolves to the same intersection whichever one you use as `search_by`. If the first page was still capped, the completion webhook returns `has_more: true` and a `message` telling you to narrow further. To fetch every page instead of narrowing, add `include: ["all_pages"]`. This makes one extra EMR call per page, so prefer a secondary filter when you can. ```bash theme={null} # Narrow a busy date of birth by name curl -X POST https://api.usecobalt.com/v1/patients/fetch \ -H "Content-Type: application/json" \ -H "client_id: your_client_id" \ -H "client_secret: your_client_secret" \ -H "access_token: your_access_token" \ -d '{ "search_by": "dob", "dob": "1985-03-15", "last_name": "Smith" }' ``` ### Response Data Patient data includes comprehensive information including active medications and associated pharmacies: ```json theme={null} { "id": "pat_123", "mrn": "123456789", "first_name": "Jane", "last_name": "Smith", "middle_name": "Marie", "date_of_birth": "1985-03-15", "address_street": "123 Market St", "address_city": "San Francisco", "address_state": "CA", "address_zip": "94102", "phone": "415-555-1234", "cell_phone": "415-555-5678", "sex": "female", "email": "jane.smith@example.com", "insurance_name": "Blue Shield of California", "insurance_subscriber_number": "ABC123456789", "insurances": [ { "emr_insurance_id": "51638", "priority": 1, "insurance_ehr_id": "733", "insurance_name": "Blue Shield of California", "insurance_subscriber_number": "ABC123456789", "group_id": "GRP12345", "plan_begin_date": "2025-01-01", "copayment": "25.00", "relationship_to_subscriber": "self", "guarantor_id": "123456", "is_guarantor_patient": true } ], "rendering_provider_id": "67890", "medications": [ { "name": "Lisinopril", "strength": "10 MG", "frequency": "Once a day", "start_date": "2025-01-21", "encounter_date": "2025-01-21", "refills": "3", "notes": "Take with food" } ], "pharmacies": [ { "pharmacy_ehr_id": "5001", "is_primary": "1", "name": "Walgreens Pharmacy", "address": "500 Geary St", "city": "San Francisco", "state": "CA", "zip": "94102", "phone": "415-555-9000" } ], "encounters": [ { "id": "3fa85f6457174562b3fc2c963f66afa6", "date": "2025-10-14", "start_time": "09:30", "visit_type": "OV", "status": "Checked Out", "provider_ehr_id": "67890", "provider_first_name": "Alice", "provider_last_name": "Nguyen", "reason": "Follow-up", "facility_name": "Market Street Clinic", "facility_id": "12", "locked": "true" } ], "encounters_has_more": false, "last_appointment_date": "2025-10-14", "problems": [ { "id": "293199", "icd10": "I10", "icd_version": "10", "name": "Essential (primary) hypertension", "status": "confirmed", "severity": "2", "onset_date": "", "added_date": "09/01/2025", "provider_name": "Nguyen, Alice", "encounter_id": "428763", "notes": "", "inactive": false } ], "medical_history": { "vitals": [ { "name": "BP", "date": "05/12/2025", "value": "120/80" }, { "name": "HR", "date": "05/12/2025", "value": "70" } ], "allergies": ["N.K.D.A"], "surgical_history": [ { "date": "2022", "reason": "Appendectomy" } ], "hospitalization": [ { "date": "2020", "reason": "Observation" } ], "family_history": [ { "relation": "Father", "description": "alive" }, { "relation": "Mother", "description": "alive" } ], "past_medical_history": ["Hypertension"], "health_maintenance": [ { "name": "Influenza", "last_done": "01/01/2025", "frequency": "1Y", "due_date": "01/01/2026", "status": "Compliant", "notes": "" } ] }, "labs": { "orders": [ { "id": "519286", "report_id": "519286", "name": "CBC", "type": 0, "order_date": "05/12/2025", "collection_date": "05/12/2025", "result_date": "05/13/2025", "ordering_physician": "Nguyen, Alice", "interface_status": "Received", "encounter_id": "461655", "facility_id": "3", "received": true, "reviewed": false } ], "results": [ { "name": "Metabolic Panel", "result_date": "05/13/2025", "analytes": [ { "name": "Glucose", "value": "95", "units": "mg/dL", "range": "70-99", "loinc": "2345-7", "previous_value": "", "status": "F" } ] } ] }, "referrals": [ { "emr_referral_id": "100611", "direction": "incoming", "referral_date": "2026-08-19", "referred_from": null, "referred_to": "Stone, Danielle M", "specialty": "Cardiology", "status": "insuranceAuth", "subtype": "Visit", "reason": "PLAQUE 75577 AUTH APPROVED TO BE DONE SHC", "diagnoses": [ { "ehr_id": "507464", "code": "I10", "description": "HTN (hypertension)" } ] } ], "alerts": { "billing_alert": false, "billing_notes": null, "global_alerts": [ { "name": "Allergy: Penicillin", "alert_type": "Allergy", "notes": "Hives", "priority": "High", "expiry_date": null } ] }, "collection_status": "C", "guarantors": [ { "id": "12345", "name": "John Smith", "dob": "1960-04-22", "relationship": "spouse", "is_guarantor_patient": false } ], "patient_balance": 0, "account_balance": 125.50 } ``` ### Insurances Array `insurances` is only returned when `include` contains `insurances`. | Field | Type | Description | | - | - | - | | `emr_insurance_id` | string | Stable EMR insurance record ID. Use this value when calling `PATCH /v1/patients/:patient_mrn/insurances/:emr_insurance_id`. | | `priority` | integer | EMR priority / sequence number for the insurance record | | `insurance_ehr_id` | string | EMR-native carrier/company ID | | `insurance_name` | string | Carrier name for this specific insurance record | | `insurance_subscriber_number` | string | Subscriber/member number for this specific insurance record | | `group_id` | string | Insurance group number | | `plan_begin_date` | string | Coverage start date | | `copayment` | string \| null | Copayment value returned by the EMR | | `relationship_to_subscriber` | string \| null | Relationship to the subscriber | | `guarantor_id` | string \| null | EMR guarantor identifier associated with the insurance | | `is_guarantor_patient` | boolean | Whether the guarantor is the patient | The top-level `insurance_name` and `insurance_subscriber_number` fields remain convenience fields on the patient object. When present, prefer `insurances[]` for record-specific insurance details and update targeting. ### Guarantors Array Each entry represents a guarantor on the patient's account. Empty array if the lookup fails. | Field | Type | Description | | - | - | - | | `id` | string | Guarantor's EMR patient ID (if the guarantor is also a patient) | | `name` | string | Guarantor's full name | | `dob` | string | Date of birth | | `relationship` | string | Relationship to the patient: `self`, `spouse`, `child`, or `other` | | `is_guarantor_patient` | boolean | Whether this guarantor is also the patient on the account | The top-level `patient_balance` and `account_balance` fields (numbers, in dollars) are returned alongside the guarantors. Both are `null` if the lookup fails. ### Alerts Object Contains the patient's billing and global alerts. May be `null` if the alerts lookup fails. | Field | Type | Description | | - | - | - | | `billing_alert` | boolean | Whether a billing alert is set on the patient | | `billing_notes` | string \| null | Free-text notes attached to the billing alert | | `global_alerts` | array | Global alerts; each has `name`, `alert_type`, `notes`, `priority`, `expiry_date` | ### Medications Array Each patient includes an array of active medications with the following fields: | Field | Type | Description | | - | - | - | | `name` | string | Medication name | | `strength` | string | Medication strength/dosage | | `frequency` | string | How often to take the medication | | `start_date` | string | Date medication was started (MM/DD/YYYY) | | `refills` | string | Number of refills remaining | | `notes` | string | Additional notes about the medication | ### Pharmacies Array Each patient includes an array of associated pharmacies with the following fields: | Field | Type | Description | | - | - | - | | `pharmacy_id` | string | Pharmacy's EMR ID | | `is_primary` | string | Whether this is the patient's primary pharmacy ("0" or "1") | | `name` | string | Pharmacy name | | `address` | string | Pharmacy street address | | `city` | string | Pharmacy city | | `state` | string | Pharmacy state code | | `zip` | string | Pharmacy ZIP code | | `phone` | string | Pharmacy phone number | ### Encounters Array Each patient includes an array of encounters (past and upcoming appointments) returned by the EMR. Field availability varies by EMR and visit type. | Field | Type | Description | | - | - | - | | `id` | string | Cobalt appointment ID for the encounter | | `ehr_appointment_id` | string | Encounter ID in the EMR | | `date` | string | Encounter date (YYYY-MM-DD) | | `start_time` | string | Scheduled start time (HH:MM, 24-hour) | | `visit_type` | string | EMR visit type code (e.g. `OV` for office visit, `TEL` for telephone encounter) | | `status` | string | Encounter status (e.g. `Checked Out`, `Scheduled`) | | `provider_ehr_id` | string | Rendering provider's EMR ID | | `provider_first_name` | string | Rendering provider's first name | | `provider_last_name` | string | Rendering provider's last name | | `reason` | string | Reason for visit | | `facility_name` | string | Facility name | | `facility_id` | string | Facility EMR ID | | `locked` | string | Whether the encounter is locked (`"true"` / `"false"`) | | `visit_note_document_id` | string | Cobalt document ID of the encounter's visit note. Pass it to [POST /documents/fetch](/api-reference/documents/fetch) to get the note as a PDF. | Telephone encounters (`visit_type: "TEL"`) are enriched with additional fields including `caller`, `message`, `actions`, `notes`, `assigned_to`, `answered_by`, `priority`, and `has_attachment`. The top-level `last_appointment_date` field is derived from the encounters array and reflects the most recent appointment date for the patient. ### Documents Array Documents in the patient's chart. Field availability varies by EMR. | Field | Type | Description | | - | - | - | | `id` | string | Cobalt document ID. Pass it to [POST /documents/fetch](/api-reference/documents/fetch) to get the file. Stable across fetches. | | `name` | string | Document name | | `description` | string | Document description | | `folder` | string | Chart folder | | `subfolder` | string | Chart subfolder | | `date` | string | Document date (YYYY-MM-DD) | | `file_name` | string | Stored file name in the EMR | The file itself is not included in the fetch. `GET /documents/{id}` returns `409` until the document has been fetched with `POST /documents/fetch`. ### Tasks Array Returned only when `include` contains `tasks` (or `tasks.notes`). Each entry is a task from the patient's chart. Some EMRs model telephone encounters as tasks with `category: "Telephone Encounter"` — use `category` to distinguish task types. All task types are returned; filter client-side on `category` if you only want a subset. | Field | Type | Description | | - | - | - | | `emr_task_id` | string | The task's EMR ID. Use this value as `emr_task_id` when calling `POST /v1/tasks/notes`. | | `follow_up_date` | string \| null | Follow-up date for the task | | `status` | string \| null | Task status (e.g. `New`, `Complete`) | | `tasking_id` | string \| null | Recipient tasking identifier | | `priority` | string \| null | Task priority | | `datetime` | string \| null | Task creation date/time | | `subject` | string \| null | Task subject line | | `recipients` | string \| null | Display names of the task recipients | | `recipient_ids` | string \| null | Recipient identifiers | | `category` | string \| null | Task type/category (e.g. `General`, `Telephone Encounter`) | | `sent_by` | string \| null | Who the task was sent by | | `notes` | array | Note history for the task. Present only when `include` contains `tasks.notes`. See below. | #### Task Notes Array Each task's `notes[]` (present only with `tasks.notes`) contains its note history, most recent first: | Field | Type | Description | | - | - | - | | `emr_note_id` | string | The note's EMR ID | | `created_date` | string \| null | When the note was created | | `description` | string \| null | Note body. Some EMRs prefix the author, e.g. `"ACME Health AI Account - call back tomorrow"`. | ```json theme={null} { "tasks": [ { "emr_task_id": "9bef4113-e9ce-4895-9a17-2c18cd625116", "follow_up_date": "6/15/2026", "status": "New", "tasking_id": "3610210f-6f36-4cfc-b7b5-79fe7fc42d83", "priority": "Normal", "datetime": "6/15/2026 12:02 PM", "subject": "Follow up call", "recipients": "ACME Management", "recipient_ids": "3610210f-6f36-4cfc-b7b5-79fe7fc42d83", "category": "Telephone Encounter", "sent_by": "ACME Health AI Account", "notes": [ { "emr_note_id": "f862b0a0-d820-418c-9e30-412bb62bde34", "created_date": "6/15/2026 12:02 PM", "description": "ACME Health AI Account - patient called back, scheduled for next week" } ] } ] } ``` ### Webhook Notifications When the patient fetch is complete, we will send a webhook to your registered endpoint. Here is an example of what the webhook payload will look like: ```json theme={null} { "id": "evt_1J9X2q2eZvKYlo2Cmnopqr", "access_token_reference_id": "user_1J9X2q2eZvKYlo2Cstuv", "job_id": "job_1J9X2q2eZvKYlo2Cmnopqr", "object": "event", "created": "2023-10-28T11:00:00Z", "timestamp": "2023-10-28T11:00:00Z", "type": "patient.live_fetch_completed", "action": "sync", "data": { "success": true, "patient_count": 20, "has_more": true, "message": "More results are available than were returned. Narrow the search with an additional filter (for example dob or name), or set include=all_pages to retrieve every page.", "patients": [ { "id": "pat_123", "mrn": "123456789", "first_name": "Jane", "last_name": "Smith", "date_of_birth": "1985-03-15", "medications": [...], "pharmacies": [...], "encounters": [...], "document_folders": [...], "last_appointment_date": "2025-10-14" } ] } } ``` `has_more` is `true` only when a name or dob search was capped and more matches exist (it is absent or `false` otherwise, and when `include: ["all_pages"]` fetched everything). `message` is present only when `has_more` is `true`. # Get Patients Source: https://docs.usecobalt.com/api-reference/patients/get GET /patients Returns a list of patients with optional filtering and pagination. ### Example Request ```bash theme={null} curl -X GET https://api.usecobalt.com/v1/patients \ -H 'Content-Type: application/json' \ -H 'client_id: ci_live_198908HJDKJSH98789OHKJL' \ -H 'client_secret: cs_live_9827hofdsklOYYHJLJh' \ -H 'access_token: 493JKLHIU98789hLKH9HHJH' \ -G \ --data-urlencode "first_name=John" \ --data-urlencode "last_name=Doe" \ --data-urlencode "page=2" \ --data-urlencode "page_size=30" \ --data-urlencode "sort=-created_at" ``` ### Example Response ```json theme={null} { "success": true, "patients": [ { "id": "65ed04f9d25fca3876f367d9ad94e3c3", "ehr_id": "123456", "mrn": "414421", "first_name": "John", "last_name": "Doe", "dob": "1980-05-15", "sex": "male", "phone": "555-123-4567", "cell_phone": "555-123-4568", "home_phone": null, "work_phone": null, "phone_numbers": { "primary": "555-123-4567", "cell": "555-123-4568", "home": null, "work": null }, "insurance_name": "Aetna", "insurance_subscriber_number": "ABC123456", "insurances": [ { "emr_insurance_id": "102", "priority": 1, "insurance_name": "Aetna", "insurance_policy_type": "Commercial", "insurance_subscriber_number": "ABC123456", "group_id": "A0001" } ], "address_street": "123 Main St", "address_city": "Springfield", "address_state": "IL", "address_zip": "62701", "pcp_first_name": "Jane", "pcp_last_name": "Smith", "status": "active", "created_at": "2024-09-01T14:30:00Z", "object_metadata": { "source": "cobalt_operation", "created_at": "2024-09-01T14:30:00Z" } } ], "pagination": { "current_page": 2, "total_pages": 5, "total_count": 150, "page_size": 30 } } ``` All filter parameters are optional. If no filters are provided, the endpoint returns all patients, paginated according to the default values or specified page and page\_size. To resolve a patient you already track by an external identifier, filter by `mrn` or `ehr_id` (both are exact-match): the response includes Cobalt's `id` alongside `ehr_id` and `mrn`. `ehr_id` is the patient's EHR-native identifier. It is `null` while a patient is still pending creation or failed in the EHR, and is populated once the EHR record exists. `object_metadata.source` reports provenance: `cobalt_operation`, `ehr_sync`, or `unknown`. # Add Patient Insurance Source: https://docs.usecobalt.com/api-reference/patients/insurances/add POST /patients/{patient_mrn}/insurances Queue the addition of insurance information to a patient record in the EMR system. This is an asynchronous operation. This endpoint queues the addition of insurance information to a patient record in the EMR system. The operation is asynchronous and returns immediately with a `job_id` for tracking. Results are delivered via webhook. After a successful create, the success webhook includes `emr_insurance_id`. This is the same record identifier returned later as `emr_insurance_id` in `POST /v1/patients/fetch` with `include: ["insurances"]`, and it can be used with the insurance update endpoint. `member_id` is the subscriber/member number you provide (the policy number from the insurance card), not an EHR-generated value. The EHR-generated record identifier is `emr_insurance_id`, returned on the webhook after the record is created. ## Insurance Provider Resolution The API supports three methods for identifying the insurance provider (in order of priority): 1. **By Cobalt Provider ID** (Most specific, recommended): ```json theme={null} { "insurance_provider_id": "abc123def456" } ``` 2. **By Name + Payer ID** (Most specific when ID not available): ```json theme={null} { "insurance_name": "ACME Health", "insurance_payer_id": "98999" } ``` 3. **By Name Only** (Least specific, may match first result if multiple exist): ```json theme={null} { "insurance_name": "ACME Health" } ``` Use `GET /v1/insurance-providers` to retrieve the correct insurance provider identifiers for your organization. ## Validation Rules ### Address Validation * **State**: Must be a valid two-letter US state abbreviation (accepts lowercase, e.g., "ca" or "CA") * **ZIP Code**: Must be either 5 digits (e.g., "90001") or 9 digits with hyphen (e.g., "90001-1234") ### Date Validation * **plan\_begin\_date**: Must be in YYYY-MM-DD format (e.g., "2025-01-15") * **plan\_end\_date**: Optional. Must be in YYYY-MM-DD format (e.g., "2025-12-31"). Omit for open-ended coverage. ### Priority (Optional) * **priority**: Insurance priority level (valid values: "1", "2", "3"). When not provided, the EMR will use its default priority setting. ### Relationship to Subscriber Accepts both codes and names (case-insensitive): * **Codes**: "01", "02", "32", "33", "17", "20", "53", "25" * **Names**: "self", "spouse", "mother", "father", "step parent", "employee", "life partner", "other" ### Insurance Provider Validation * `insurance_payer_id` alone is not supported (must be combined with `insurance_name`) * If provider ID, name, or name+payer\_id combination is not found, returns 404 with helpful message ## Example Requests ### Minimal Request (Using Insurance Provider ID) ```bash theme={null} curl -X POST https://api.usecobalt.com/v1/patients/MRN123456/insurances \ -H "Content-Type: application/json" \ -H "client_id: your_client_id" \ -H "client_secret: your_client_secret" \ -H "access_token: your_access_token" \ -d '{ "member_id": "MEM123456789", "group_id": "GRP98999", "plan_begin_date": "2025-01-01", "insurance_provider_id": "abc123def456", "subscriber_demographics": { "first_name": "John", "last_name": "Doe", "street_line_1": "123 Main St", "city": "Los Angeles", "state": "CA", "zip": "90001" } }' ``` ### Complete Request (Using Insurance Name + Payer ID) ```bash theme={null} curl -X POST https://api.usecobalt.com/v1/patients/MRN123456/insurances \ -H "Content-Type: application/json" \ -H "client_id: your_client_id" \ -H "client_secret: your_client_secret" \ -H "access_token: your_access_token" \ -d '{ "member_id": "MEM123456789", "group_id": "GRP98999", "insurance_name": "ACME Health", "insurance_payer_id": "98999", "plan_begin_date": "2025-01-01", "plan_end_date": "2025-12-31", "copayment": 25, "priority": "1", "relationship_to_subscriber": "spouse", "subscriber_demographics": { "first_name": "Jane", "last_name": "Doe", "street_line_1": "123 Main St", "city": "Los Angeles", "state": "CA", "zip": "90001-1234" } }' ``` ### Self-Insured Patient with 9-Digit ZIP ```bash theme={null} curl -X POST https://api.usecobalt.com/v1/patients/MRN789012/insurances \ -H "Content-Type: application/json" \ -H "client_id: your_client_id" \ -H "client_secret: your_client_secret" \ -H "access_token: your_access_token" \ -d '{ "member_id": "MEM987654321", "group_id": "GRP11111", "insurance_name": "Blue Cross Blue Shield", "plan_begin_date": "2025-01-01", "relationship_to_subscriber": "01", "subscriber_demographics": { "first_name": "Jane", "last_name": "Smith", "street_line_1": "456 Oak Ave", "city": "San Francisco", "state": "ca", "zip": "94102-1234" } }' ``` ## Example Response ```json theme={null} { "success": true, "message": "Insurance addition queued successfully. Processing will begin shortly.", "patient_insurance_id": "xyz789abc123", "job_id": 12345 } ``` ## Webhook Events After the insurance addition is processed, a webhook event will be sent to your configured webhook URL. ### Success Event (patient.insurance.added) ```json theme={null} { "id": "evt_abc123def456", "access_token_reference_id": "your_reference_id", "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", "plan_end_date": "2025-12-31", "copayment": 25, "priority": 1, "relationship_to_subscriber": "self", "emr_insurance_id": "51638" } } ``` `emr_insurance_id` is the EMR-native insurance record ID. The same value is returned in live fetch as `emr_insurance_id` and should be used when calling `PATCH /v1/patients/:patient_mrn/insurances/:emr_insurance_id`. `audit_trail` is present in the success payload for some EMRs and absent for others, so the example above may carry more or fewer fields than your integration returns. ### Failure Event (patient.insurance.failed) ```json theme={null} { "id": "evt_xyz789abc123", "access_token_reference_id": "your_reference_id", "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" } } ``` Insurance addition is asynchronous. Store the returned `patient_insurance_id` and `job_id`, then listen for webhooks to determine the final status. # Update Patient Insurance Source: https://docs.usecobalt.com/api-reference/patients/insurances/update PATCH /patients/{patient_mrn}/insurances/{emr_insurance_id} Queue an update to an existing patient insurance record in the EMR system. This endpoint queues an asynchronous update for an existing patient insurance record. **Ending / cancelling coverage:** there is no delete endpoint for insurance records. To end coverage, PATCH the record with a `plan_end_date`. ## Identifying The Insurance To Update Use the EMR insurance record ID. You can get it from either: * `POST /v1/patients/fetch` with `include: ["insurances"]` * the `emr_insurance_id` returned in a prior `patient.insurance.added` or `patient.insurance.updated` webhook In live fetch responses, the update target is returned as `insurances[].emr_insurance_id`. ## Provider Resolution The API supports the same insurance provider resolution options as insurance create: 1. `insurance_provider_id` 2. `insurance_name` + `insurance_payer_id` 3. `insurance_name` `insurance_payer_id` alone is not supported. If you request a carrier change, the resolved insurance provider must have an EMR `ehrId` configured or the request will be rejected. ## Validation Rules * All request body fields are optional. Omitted fields are preserved from the current EMR insurance record. * `emr_insurance_id` is required in the URL path. * If you send `subscriber_demographics`, it must include `first_name` and `last_name`. * If `relationship_to_subscriber` is a non-self value such as `spouse`, `child`, or `other`, `subscriber_demographics.first_name` and `subscriber_demographics.last_name` are required. * `plan_begin_date`, when provided, must be in `YYYY-MM-DD` format. * `plan_end_date`, when provided, must be in `YYYY-MM-DD` format. When omitted, the current coverage end date is preserved (not cleared). ## Discover The Record ID First ```bash theme={null} curl -X POST https://api.usecobalt.com/v1/patients/fetch \ -H "Content-Type: application/json" \ -H "client_id: your_client_id" \ -H "client_secret: your_client_secret" \ -H "access_token: your_access_token" \ -d '{ "mrn": "MRN123456", "search_by": "mrn", "include": ["insurances"] }' ``` Example insurance entry from the live-fetch webhook payload: ```json theme={null} { "emr_insurance_id": "51638", "priority": 1, "insurance_ehr_id": "733", "insurance_name": "Alignment Health Plan(P)", "insurance_subscriber_number": "MEM-POSTMAN-001", "group_id": "GRP-POSTMAN-001", "plan_begin_date": "2026-07-01", "plan_end_date": "2027-06-30", "copayment": "10.00", "relationship_to_subscriber": "spouse", "guarantor_id": "90688", "is_guarantor_patient": false } ``` ## Example Requests ### Update A Few Insurance Fields ```bash theme={null} curl -X PATCH https://api.usecobalt.com/v1/patients/MRN123456/insurances/51638 \ -H "Content-Type: application/json" \ -H "client_id: your_client_id" \ -H "client_secret: your_client_secret" \ -H "access_token: your_access_token" \ -d '{ "member_id": "MEM123456789", "group_id": "GRP98999", "plan_begin_date": "2025-01-01", "copayment": 25 }' ``` ### Update Coverage Dates ```bash theme={null} curl -X PATCH https://api.usecobalt.com/v1/patients/MRN123456/insurances/51638 \ -H "Content-Type: application/json" \ -H "client_id: your_client_id" \ -H "client_secret: your_client_secret" \ -H "access_token: your_access_token" \ -d '{ "plan_begin_date": "2026-01-01", "plan_end_date": "2026-12-31" }' ``` ### Change To A Non-Self Subscriber ```bash theme={null} curl -X PATCH https://api.usecobalt.com/v1/patients/MRN123456/insurances/51638 \ -H "Content-Type: application/json" \ -H "client_id: your_client_id" \ -H "client_secret: your_client_secret" \ -H "access_token: your_access_token" \ -d '{ "relationship_to_subscriber": "spouse", "subscriber_demographics": { "first_name": "Jane", "last_name": "Doe", "dob": "1985-04-12", "phone": "5551234567", "sex": "F" } }' ``` ### Change The Carrier ```bash theme={null} curl -X PATCH https://api.usecobalt.com/v1/patients/MRN123456/insurances/51638 \ -H "Content-Type: application/json" \ -H "client_id: your_client_id" \ -H "client_secret: your_client_secret" \ -H "access_token: your_access_token" \ -d '{ "insurance_name": "ACE Medicare Supplement", "insurance_payer_id": "14367" }' ``` ## Example Response ```json theme={null} { "success": true, "message": "Insurance update queued successfully. Processing will begin shortly.", "patient_insurance_id": "xyz789abc123", "job_id": 12345 } ``` ## Webhook Events ### Success Event (patient.insurance.updated) ```json theme={null} { "id": "evt_abc123def456", "access_token_reference_id": "your_reference_id", "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", "plan_end_date": "2025-12-31", "copayment": 25, "priority": 1, "relationship_to_subscriber": "spouse", "emr_insurance_id": "51638" } } ``` ### Failure Event (patient.insurance.failed) ```json theme={null} { "id": "evt_xyz789abc123", "access_token_reference_id": "your_reference_id", "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": "No existing insurance found with emr_insurance_id 51638 for patient MRN123456" } } ``` # Update Patient Source: https://docs.usecobalt.com/api-reference/patients/update PATCH /patients/{id} Updates an existing patient. The update process differs based on the current status of the patient. **Use the Cobalt `id` in the URL path.** The `{id}` parameter should be the Cobalt patient ID returned from API responses or GET endpoints, not the MRN or EHR ID. The update process differs based on the current status of the patient: ### 1. Updating Pending or Failed Patients For patients with a status of 'pending' or 'failed', the patient does not yet exist in the EHR. You can update any attribute, and the new values will be used the next time the patient creation runs. | Parameter | Type | Required | Description | | - | - | - | - | | Any patient attribute | varies | No | Any attribute of the patient can be updated. All fields are optional. | ### 2. Updating Active Patients For patients with a status of 'active' (already created in the EHR), you can update the patient's contact information, address, and provider assignments. The change is queued and applied to the EHR asynchronously; the patient's status will move to 'pending\_update' while the update is in flight and back to 'active' once the EHR write completes. #### Contact & Address Fields | Parameter | Type | Required | Description | | - | - | - | - | | `phone` | string | No | Home phone, formatted as `222-333-4444`. | | `cell_phone` | string | No | Cell phone, formatted as `222-333-4444`. | | `email` | string | No | Patient email address. | | `address_street` | string | No | Street address line 1. | | `address_line2` | string | No | Address line 2 (apartment, suite, unit, etc.). | | `address_city` | string | No | City. | | `address_state` | string | No | Two-letter state code (e.g. `CA`). DC is accepted. | | `address_zip` | string | No | ZIP code. Accepts 5-digit (`12345`) or ZIP+4 (`12345-6789`) formats. | | `save_previous_address` | boolean | No | When `true` (default), the patient's current address is saved as address history in the EHR before the new address is applied. Set to `false` to overwrite without preserving history. | #### Provider Fields | Parameter | Type | Required | Description | | - | - | - | - | | `referring_provider_first_name` | string | No | Referring provider's first name. Must be used with `referring_provider_last_name`. | | `referring_provider_last_name` | string | No | Referring provider's last name. Must be used with `referring_provider_first_name`. | | `referring_provider_id` | string | No | Cobalt referring provider UUID (from `GET /v1/referring-providers`). Looks up the provider's name and contact info for more accurate matching. | | `pcp_first_name` | string | No | PCP's first name. Must be used with `pcp_last_name`. | | `pcp_last_name` | string | No | PCP's last name. Must be used with `pcp_first_name`. | | `pcp_id` | string | No | Cobalt referring provider UUID (from `GET /v1/referring-providers`). Looks up the provider's name and contact info for more accurate matching. | | `referred_to_provider_id` | string | No | Rendering provider EHR ID — use the `ehr_id` value from `GET /v1/providers` (not the `id`). | #### Demographics & Contact Preference Fields | Parameter | Type | Required | Description | | - | - | - | - | | `language` | string | No | Patient preferred language. Must match a valid language name in your EHR instance. | | `race` | string | No | Patient race. Must match a valid race name in your EHR instance. | | `ethnicity` | string | No | Patient ethnicity. Must match a valid ethnicity code or display name in your EHR instance. | | `voice_enabled` | boolean | No | Enables or disables voice (phone) notifications in the EHR patient communication settings. | | `text_enabled` | boolean | No | Enables or disables text (SMS) notifications in the EHR patient communication settings. | | `billing_notes` | string | No | The patient's billing alert notes (free text). The value you send replaces the current notes verbatim; the same field is returned as `billing_notes` by `POST /v1/patients/fetch`. | | `pharmacy_id` | string | No | The patient's primary pharmacy, as the EMR pharmacy ID (the `ehr_id` from `GET /v1/pharmacies`). Adds the pharmacy as the primary, keeping any existing pharmacies and demoting the prior primary. | | `self_pay` | boolean | No | Marks the patient as self-pay. Accepts `true` or `false`. A provider-only update never changes this flag. | **Billing notes.** A common workflow: read the current `billing_notes` via patient fetch, transform it (for example, rewrite `referral: ...;` to `scheduledreferral: ...;`), then PATCH it back. **Notification language.** When `voice_enabled` or `text_enabled` is set, the notification language in the EHR follows the patient's `language` (using the new value if `language` is included in the same request, otherwise the patient's current language): Spanish when `language` contains "spanish" (case-insensitive), otherwise English. These are the only two options exposed for communication notifications. Contact/address fields, provider fields, demographics, and contact preferences are applied independently. If one group is rejected by the EHR (for example a provider name that cannot be resolved, or a record the EHR will not let you modify), the other groups in the same request are still applied. Anything that could not be applied is returned in a `failed_fields` array on the `patient.updated` webhook, along with a human-readable `message`. If no fields could be applied, the request fails and a `patient.update_failed` webhook is sent instead. Use `referring_provider_id` or `pcp_id` instead of names when possible, as ID-based resolution is more reliable. At least one data field must be provided. ### Example Request for Updating a Pending/Failed Patient ```bash theme={null} curl -X PATCH https://api.usecobalt.com/v1/patients/65ed04f9d25fca3876f367d9ad94e3c3 \ -H 'Content-Type: application/json' \ -H 'client_id: ci_live_198908HJDKJSH98789OHKJL' \ -H 'client_secret: cs_live_9827hofdsklOYYHJLJh' \ -H 'access_token: 493JKLHIU98789hLKH9HHJH' \ -d '{ "phone": "555-987-6543", "insurance_name": "Aetna", "insurance_subscriber_number": "ABC987654321" }' ``` ### Example Request for Updating an Active Patient's Contact Info ```bash theme={null} curl -X PATCH https://api.usecobalt.com/v1/patients/65ed04f9d25fca3876f367d9ad94e3c3 \ -H 'Content-Type: application/json' \ -H 'client_id: ci_live_198908HJDKJSH98789OHKJL' \ -H 'client_secret: cs_live_9827hofdsklOYYHJLJh' \ -H 'access_token: 493JKLHIU98789hLKH9HHJH' \ -d '{ "phone": "555-987-6543", "cell_phone": "555-987-6544", "email": "patient@example.com" }' ``` ### Example Request for Updating an Active Patient's Address ```bash theme={null} curl -X PATCH https://api.usecobalt.com/v1/patients/65ed04f9d25fca3876f367d9ad94e3c3 \ -H 'Content-Type: application/json' \ -H 'client_id: ci_live_198908HJDKJSH98789OHKJL' \ -H 'client_secret: cs_live_9827hofdsklOYYHJLJh' \ -H 'access_token: 493JKLHIU98789hLKH9HHJH' \ -d '{ "address_street": "456 New St", "address_line2": "Apt 7", "address_city": "Austin", "address_state": "TX", "address_zip": "78701" }' ``` To update the address without saving the previous address as history, pass `save_previous_address: false`: ```bash theme={null} curl -X PATCH https://api.usecobalt.com/v1/patients/65ed04f9d25fca3876f367d9ad94e3c3 \ -H 'Content-Type: application/json' \ -H 'client_id: ci_live_198908HJDKJSH98789OHKJL' \ -H 'client_secret: cs_live_9827hofdsklOYYHJLJh' \ -H 'access_token: 493JKLHIU98789hLKH9HHJH' \ -d '{ "address_street": "456 New St", "address_city": "Austin", "address_state": "TX", "address_zip": "78701", "save_previous_address": false }' ``` ### Example Request for Updating an Active Patient's Providers ```bash theme={null} curl -X PATCH https://api.usecobalt.com/v1/patients/65ed04f9d25fca3876f367d9ad94e3c3 \ -H 'Content-Type: application/json' \ -H 'client_id: ci_live_198908HJDKJSH98789OHKJL' \ -H 'client_secret: cs_live_9827hofdsklOYYHJLJh' \ -H 'access_token: 493JKLHIU98789hLKH9HHJH' \ -d '{ "referring_provider_id": "e35e82b85df58f08a9b38e06a91e9f1a", "pcp_id": "223ecd1b29e546ca9db6b391eb49b213", "referred_to_provider_id": "82749" }' ``` Or using names instead of IDs: ```bash theme={null} curl -X PATCH https://api.usecobalt.com/v1/patients/65ed04f9d25fca3876f367d9ad94e3c3 \ -H 'Content-Type: application/json' \ -H 'client_id: ci_live_198908HJDKJSH98789OHKJL' \ -H 'client_secret: cs_live_9827hofdsklOYYHJLJh' \ -H 'access_token: 493JKLHIU98789hLKH9HHJH' \ -d '{ "referring_provider_first_name": "Jane", "referring_provider_last_name": "Doe", "pcp_first_name": "Robert", "pcp_last_name": "Johnson" }' ``` ### Example Request for Updating an Active Patient's Demographics & Contact Preferences ```bash theme={null} curl -X PATCH https://api.usecobalt.com/v1/patients/65ed04f9d25fca3876f367d9ad94e3c3 \ -H 'Content-Type: application/json' \ -H 'client_id: ci_live_198908HJDKJSH98789OHKJL' \ -H 'client_secret: cs_live_9827hofdsklOYYHJLJh' \ -H 'access_token: 493JKLHIU98789hLKH9HHJH' \ -d '{ "first_name": "Jane", "last_name": "Doe", "dob": "1980-01-15", "language": "Spanish", "race": "White", "ethnicity": "Hispanic or Latino", "voice_enabled": true, "text_enabled": true }' ``` ### Example Request for Marking a Patient as Self-Pay ```bash theme={null} curl -X PATCH https://api.usecobalt.com/v1/patients/65ed04f9d25fca3876f367d9ad94e3c3 \ -H 'Content-Type: application/json' \ -H 'client_id: ci_live_198908HJDKJSH98789OHKJL' \ -H 'client_secret: cs_live_9827hofdsklOYYHJLJh' \ -H 'access_token: 493JKLHIU98789hLKH9HHJH' \ -d '{ "self_pay": true }' ``` ### Example Response ```json theme={null} { "success": true, "message": "Patient update queued.", "patient_id": "65ed04f9d25fca3876f367d9ad94e3c3", "job_id": 12345 } ``` `job_id` is the API log ID for the request and is also present on the resulting `patient.updated` webhook, so you can match either identifier back to this call. Only include the fields you want to update in the request body. Omitted fields will remain unchanged. # Create Payment Source: https://docs.usecobalt.com/api-reference/payments/create POST /payments Posts a patient payment back to the provider's EMR ledger. Posting payments is an asynchronous operation. The endpoint validates your request, queues the payment, and returns immediately with a `payment_id` and `job_id`. The payment is posted to the EMR by a background worker, and a webhook event is sent to your registered endpoint upon completion. ### Request Parameters #### Required Fields * **patient\_mrn** (string, required): Medical Record Number (MRN) of the patient the payment belongs to. * **payment\_method** (string, required): Name of the payment method as configured in the EMR (e.g. `"Card"`). The worker resolves this name to the EMR's internal payment method id at post time. * **reference\_number** (string, required): Your reference for the payment (e.g. a transaction or receipt number). Reference numbers may be reused across payments — they are not used as a uniqueness key. * **location** (string, required): Identifier of the EMR location/facility the payment is posted against. * **amount** (number, required): Payment amount. Must be a positive number. #### Optional Fields * **notes** (string, optional): Free-text note recorded with the payment in the EMR. * **deposit\_date** (string, optional): Deposit date in `YYYY-MM-DD` format. Defaults to today if omitted. ### Example Request ```bash theme={null} curl -X POST https://api.usecobalt.com/v1/payments \ -H 'Content-Type: application/json' \ -H 'client_id: ci_live_198908HJDKJSH98789OHKJL' \ -H 'client_secret: cs_live_9827hofdsklOYYHJLJh' \ -H 'access_token: 493JKLHIU98789hLKH9HHJH' \ -d '{ "patient_mrn": "MRN-12345", "payment_method": "Card", "reference_number": "#123456", "location": "1404", "amount": 350, "notes": "Patient copay collected at front desk", "deposit_date": "2026-06-15" }' ``` ### Example Response ```json theme={null} { "success": true, "message": "Payment processing. A webhook event will be sent upon completion.", "payment_id": "123e4567e89b12d3a456426614174001", "job_id": 12345 } ``` The response includes: * **payment\_id**: Unique identifier for the created payment record. * **job\_id**: Job execution identifier for tracking the async operation. ### Error Responses #### Missing or Invalid Field If a required field is missing or invalid, the API returns a `400` with the specific field name: ```json theme={null} { "success": false, "message": "Missing or invalid required field: amount (must be a positive number)" } ``` #### Patient Not Found If the patient MRN cannot be resolved to an EMR patient, the API returns a `404`: ```json theme={null} { "success": false, "message": "Patient with MRN 'MRN-12345' not found. Please ensure the patient has been synced and has an EMR patient ID." } ``` #### User Not Found ```json theme={null} { "success": false, "message": "User not found." } ``` ### Webhook Notifications When the payment posting is complete, we will send a webhook to your registered endpoint. Here are examples of what those webhook payloads will look like: #### Success ```json theme={null} { "id": "evt_1J9X2q2eZvKYlo2CluR9g9gV", "access_token_reference_id": "user_1J9X2q2eZvKYlo2Cxyz", "object": "event", "created": "2026-06-15T10:30:00Z", "type": "payment.created", "job_id": "12345", "data": { "payment_id": "123e4567e89b12d3a456426614174001", "emr_transaction_id": "13969262", "payment_method": "Card", "amount": 350, "reference_number": "#123456", "patient_mrn": "MRN-12345" } } ``` #### Failure ```json theme={null} { "id": "evt_1J9X2q2eZvKYlo2CluR9g9gW", "access_token_reference_id": "user_1J9X2q2eZvKYlo2Cxyz", "object": "event", "created": "2026-06-15T10:35:00Z", "type": "payment.failed", "job_id": "12345", "data": { "payment_id": "123e4567e89b12d3a456426614174001", "failure_reason": "Failed to post payment" } } ``` # Get Pharmacies Source: https://docs.usecobalt.com/api-reference/pharmacies/get GET /pharmacies Retrieve pharmacies from the EMR system for your organization. ## Example Request ```bash theme={null} curl -X GET "https://api.usecobalt.com/v1/pharmacies" \ -H "client_id: your_client_id" \ -H "client_secret: your_client_secret" \ -H "access_token: your_access_token" ``` ## Example Response ```json theme={null} { "success": true, "pharmacies": [ { "id": "abc123def456ghi789", "ehr_id": "12345", "ncpdp_id": "5846247", "name": "Main Street Pharmacy", "address": { "address1": "123 Main St", "city": "San Francisco", "state": "CA", "zip": "94102" }, "phone": "(415) 555-0123", "email": "info@mainstreetpharmacy.com", "status": "active" }, { "id": "xyz789ghi012jkl345", "ehr_id": "67890", "ncpdp_id": "5702724", "name": "Downtown Medical Pharmacy", "address": { "address1": "456 Market St", "city": "San Francisco", "state": "CA", "zip": "94105" }, "phone": "(415) 555-0456", "email": "contact@downtownmedpharm.com", "status": "active" } ] } ``` ## Response Fields ### Top-Level Fields * **success** (boolean): Whether the request was successful * **pharmacies** (array): Array of pharmacy objects for the organization ### Pharmacy Object * **id** (string): Cobalt pharmacy ID (UUID without dashes) * **ehr\_id** (string): The pharmacy ID as it appears in the EMR system * **ncpdp\_id** (string): The pharmacy's NCPDP ID (standard pharmacy identifier) * **name** (string): Pharmacy name * **address** (object): Pharmacy address information * **address1** (string): Street address * **city** (string): City name * **state** (string): State abbreviation * **zip** (string): ZIP code * **phone** (string): Contact phone number * **email** (string): Contact email address * **status** (string): Pharmacy status - either "active" or "inactive" # Get Providers Source: https://docs.usecobalt.com/api-reference/providers/get GET /providers Returns a list of providers associated with a clinic. ### Understanding Provider IDs Each provider has two identifiers: * **`id`**: Cobalt's internal identifier (32-character UUID without hyphens) * Use when updating provider settings like status or hours * Operations: `PATCH /v1/providers/{id}` * **`ehr_id`**: Your EMR system's provider identifier * Use when creating appointments or other EMR operations * Operations: `POST /v1/appointments` (`provider` field) **Why two IDs?** Different operations work in different contexts. Provider management (updating status, hours) modifies Cobalt's cached configuration, while appointment creation communicates directly with your EMR. Cobalt uses its own IDs to manage provider settings independently of EMR constraints, then maps to EMR IDs when interacting with your EMR system. **Quick Reference:** * Updating provider settings → Use `id` * Creating appointments → Use `ehr_id` ### Example Request ```bash theme={null} curl -X GET https://api.usecobalt.com/v1/providers \ -H 'Content-Type: application/json' \ -H 'client_id: ci_live_198908HJDKJSH98789OHKJL' \ -H 'client_secret: cs_live_9827hofdsklOYYHJLJh' \ -H 'access_token: 493JKLHIU98789hLKH9HHJH' ``` To resolve a provider's Cobalt `id` from an EHR-native identifier you already hold (for example when setting `pcp_id` on a patient), filter by `ehr_id`: ```bash theme={null} curl -X GET https://api.usecobalt.com/v1/providers \ -H 'Content-Type: application/json' \ -H 'client_id: ci_live_198908HJDKJSH98789OHKJL' \ -H 'client_secret: cs_live_9827hofdsklOYYHJLJh' \ -H 'access_token: 493JKLHIU98789hLKH9HHJH' \ -G \ --data-urlencode "ehr_id=99999" ``` ### Example Response ```json theme={null} { "success": true, "providers": [ { "id": "abc123def4567890abcdef1234567890", "ehr_id": "99999", "timezone": "America/Phoenix", "npi": "425345345", "specialty": "Family Medicine", "credentials": "MD", "name": "Doe, John", "status": "active", "hide_in_availability": false, "hours": [ { "day": "Monday", "shifts": [ { "end": "T12:00:00", "start": "T08:00:00", "set_start_date": "2024-11-20", "set_end_date": "2025-11-20", "facility_id": "2", "facility_name": "Acme Clinic", "recurrence": { "rec_start_date": "2024-11-20", "recur_interval_type": "weeks", "recur_interval_amount": 1, "recur_interval_description": "Every 1 week." }, "visit_type_rules": [ { "end_time": "13:45:00", "start_time": "13:00:00", "total_visits": 1, "visit_type_id": "NP", "visit_type_desc": "New Patient" }, ] }, { "end": "T16:30:00", "start": "T13:00:00" } ] }, ] } ] } ``` ### Response Parameters #### Provider Fields * **id**: Cobalt's internal provider identifier (used for updating provider settings) * **ehr\_id**: EMR system's provider identifier (used for creating appointments) * **timezone**: Provider's timezone * **npi**: National Provider Identifier * **name**: Provider's name * **status**: Provider status (`active` or `inactive`) * `active`: Provider's schedule will be synced from the EMR * `inactive`: Provider's schedule will not be synced * **hide\_in\_availability**: Whether provider is hidden from availability results (boolean, defaults to `false`) * `false`: Provider appears in `/v1/availability` endpoint results (default behavior) * `true`: Provider's schedule is synced but hidden from availability results * **Use case**: Set to `true` when you want to continue syncing a provider's schedule (for reporting/historical purposes) but don't want them to appear as available for new appointments * **Important**: For a provider to appear in availability, BOTH conditions must be true: 1. `active` must be `true` (schedule is being synced) 2. `hide_in_availability` must be `false` (not explicitly hidden) #### Schedule Structure * **Day**: Specifies which day of the week the schedule applies to * **Shifts**: Array of work periods with: * Start/end times using 24-hour format. Times are in the clinic's EMR timezone. * Facility information (name and ID) * Date range when the schedule is active (start date and optional end date. If there is no end date then the shift is active indefinitely). * **Visit Type Rules** (optional): * Pre-allocated slots for specific appointment types * Each slot has its own start/end times * Includes description and total allowed appointments * **Recurrence** (optional): * Defines repeating patterns (e.g., "every 2 weeks") * Contains start date and interval information * Allows for schedules that don't occur every week # Update Provider Source: https://docs.usecobalt.com/api-reference/providers/update PATCH /providers/{id} Updates a provider's status, working hours, and/or availability visibility. **Use the Cobalt `id` in the URL path.** The `{id}` parameter should be the Cobalt provider ID (the `id` field from GET responses), not the `ehr_id`. If you prefer to use the EMR ID, see `PATCH /providers/ehr/{ehr_id}` instead. ### Example Request ```bash theme={null} curl -X PATCH https://api.usecobalt.com/v1/providers/abc123def4567890abcdef1234567890 \ -H 'Content-Type: application/json' \ -H 'client_id: ci_live_198908HJDKJSH98789OHKJL' \ -H 'client_secret: cs_live_9827hofdsklOYYHJLJh' \ -H 'access_token: 493JKLHIU98789hLKH9HHJH' \ -d '{ "status": "active", "hours": [ { "day": "Monday", "shifts": [ { "start": "T09:00:00", "end": "T18:00:00", "facility_id": "095f8067-d3fc-4e13-b771-32f87cd0c3f2", "set_start_date": "2025-11-13T00:00:00", "set_end_date": "2026-11-13T00:00:00" } ] }, { "day": "Tuesday", "shifts": [ { "start": "T08:00:00", "end": "T17:00:00", "facility_id": "095f8067-d3fc-4e13-b771-32f87cd0c3f2", "set_start_date": "2025-11-13T00:00:00", "set_end_date": "2026-11-13T00:00:00" } ] } ] }' ``` **Response:** ```json theme={null} { "success": true, "message": "Provider status and hours updated successfully.", "provider_id": "abc123def4567890abcdef1234567890" } ``` ### Request Body Parameters You can update one or more of the following fields: * **status** (string, optional): Provider's sync status * `"active"`: Schedule will be synced from EMR * `"inactive"`: Schedule will not be synced from EMR * Status is case-insensitive ("active", "Active", "ACTIVE" are all accepted) * **hide\_in\_availability** (string, optional): Controls whether provider is hidden from availability results * `"false"`: Provider appears in `/v1/availability` endpoint (default behavior) * `"true"`: Provider is hidden from `/v1/availability` endpoint * **Common use case**: Set a provider to `"status": "active"` and `"hide_in_availability": "true"` to continue syncing their schedule (for reporting purposes) while hiding them from new appointment availability * **hours** (array, optional): Provider's working schedule * The `facility_id` must exist in your organization's locations * The `facility_name` will be automatically enriched from the database * Time format must be `THH:MM:SS` (e.g., `T08:00:00`) * Dates must be in ISO 8601 format (e.g., `2025-11-13T00:00:00`) * Days must be capitalized day names (Monday, Tuesday, etc.) ### Example: Hide Provider from Availability ```bash theme={null} curl -X PATCH https://api.usecobalt.com/v1/providers/abc123def4567890abcdef1234567890 \ -H 'Content-Type: application/json' \ -H 'client_id: ci_live_198908HJDKJSH98789OHKJL' \ -H 'client_secret: cs_live_9827hofdsklOYYHJLJh' \ -H 'access_token: 493JKLHIU98789hLKH9HHJH' \ -d '{ "status": "active", "hide_in_availability": "true" }' ``` **Response:** ```json theme={null} { "success": true, "message": "Provider status and hide_in_availability updated successfully.", "provider_id": "abc123def4567890abcdef1234567890" } ``` # Update Provider by EMR ID Source: https://docs.usecobalt.com/api-reference/providers/update-by-ehr PATCH /providers/ehr/{ehr_id} Updates a provider's status, working hours, and/or availability visibility using the EMR ID. This is an alternative to using Cobalt's internal provider ID. ### Example Request ```bash theme={null} curl -X PATCH https://api.usecobalt.com/v1/providers/ehr/12345 \ -H 'Content-Type: application/json' \ -H 'client_id: ci_live_198908HJDKJSH98789OHKJL' \ -H 'client_secret: cs_live_9827hofdsklOYYHJLJh' \ -H 'access_token: 493JKLHIU98789hLKH9HHJH' \ -d '{ "status": "active", "hours": [ { "day": "Monday", "shifts": [ { "start": "T09:00:00", "end": "T18:00:00", "facility_id": "095f8067-d3fc-4e13-b771-32f87cd0c3f2", "set_start_date": "2025-11-13T00:00:00", "set_end_date": "2026-11-13T00:00:00" } ] }, { "day": "Tuesday", "shifts": [ { "start": "T08:00:00", "end": "T17:00:00", "facility_id": "095f8067-d3fc-4e13-b771-32f87cd0c3f2", "set_start_date": "2025-11-13T00:00:00", "set_end_date": "2026-11-13T00:00:00" } ] } ] }' ``` **Response:** ```json theme={null} { "success": true, "message": "Provider status and hours updated successfully.", "provider_id": "abc123def4567890abcdef1234567890" } ``` ### Request Body Parameters You can update one or more of the following fields: * **status** (string, optional): Provider's sync status * `"active"`: Schedule will be synced from EMR * `"inactive"`: Schedule will not be synced from EMR * Status is case-insensitive ("active", "Active", "ACTIVE" are all accepted) * **hide\_in\_availability** (string, optional): Controls whether provider is hidden from availability results * `"false"`: Provider appears in `/v1/availability` endpoint (default behavior) * `"true"`: Provider is hidden from `/v1/availability` endpoint * **Common use case**: Set a provider to `"status": "active"` and `"hide_in_availability": "true"` to continue syncing their schedule (for reporting purposes) while hiding them from new appointment availability * **hours** (array, optional): Provider's working schedule * The `facility_id` must exist in your organization's locations * The `facility_name` will be automatically enriched from the database * Time format must be `THH:MM:SS` (e.g., `T08:00:00`) * Dates must be in ISO 8601 format (e.g., `2025-11-13T00:00:00`) * Days must be capitalized day names (Monday, Tuesday, etc.) ### Example: Hide Provider from Availability ```bash theme={null} curl -X PATCH https://api.usecobalt.com/v1/providers/ehr/12345 \ -H 'Content-Type: application/json' \ -H 'client_id: ci_live_198908HJDKJSH98789OHKJL' \ -H 'client_secret: cs_live_9827hofdsklOYYHJLJh' \ -H 'access_token: 493JKLHIU98789hLKH9HHJH' \ -d '{ "status": "active", "hide_in_availability": "true" }' ``` **Response:** ```json theme={null} { "success": true, "message": "Provider status and hide_in_availability updated successfully.", "provider_id": "abc123def4567890abcdef1234567890" } ``` # Create Recall Source: https://docs.usecobalt.com/api-reference/recalls/create POST /recalls Creates a recall in the connected account's EMR. Recalls are created asynchronously: a valid request returns immediately, and a `recall.created` or `recall.failed` webhook is sent when processing completes. # Add Action to Recall Source: https://docs.usecobalt.com/api-reference/recalls/create-action POST /recalls/{id}/actions Adds an action to an existing recall. A closing action closes the recall. Actions are added asynchronously: a valid request returns immediately, and a `recall_action.created` or `recall_action.failed` webhook is sent when processing completes. # Get Recalls Source: https://docs.usecobalt.com/api-reference/recalls/list GET /recalls Lists recalls for the connected account. # Update Recall Source: https://docs.usecobalt.com/api-reference/recalls/update PATCH /recalls/{id} Changes a recall's status and/or adds a note. # Create Referral Source: https://docs.usecobalt.com/api-reference/referrals/create POST /referrals Queues a referral for creation in the provider's EMR system. Referrals are created in the provider's EMR using an asynchronous processing model. Creating a referral this way is not instantaneous. Instead of leaving your POST request hanging until completion, we immediately return a success response if the request is properly formed. We then notify you via a webhook when the referral has been created in the EMR. This gives you flexibility around your user experience. For example, when you first make the `/referrals` call you can display a **Processing** status to your user, and when you receive the webhook notification you can update that to **Completed**. ### Request Parameters #### Required Fields * **patient\_mrn** (string, required): The patient's medical record number. The patient must already be synced to Cobalt. * **referring\_from\_provider\_ehr\_id** (string, required): EMR ID of the provider the referral is coming **from**. * **referring\_to\_provider\_ehr\_id** (string, required): EMR ID of the provider the referral is going **to**. * **direction** (string, required): Direction of the referral — `incoming` or `outgoing`. * **priority** (string, required): Referral priority — `routine`, `urgent`, or `stat`. * **reason** (string, required): Reason for the referral. #### Optional Fields * **diagnosis\_codes** (array of strings, optional): ICD-10 diagnosis codes associated with the referral. * **documents** (array, optional): Documents to attach to the referral in the EMR. Each entry is an object with `filename` and `content_base64`. See [Attaching Documents](#attaching-documents) below. * **prior\_authorization\_code** (string, optional): Prior authorization code to write on the referral. * **specialty** (string, optional): Referral specialty name — e.g. `"Cardiology"`. Resolved against the EMR instance's live specialty list when the referral is processed; an unrecognized specialty fails with a `referral.failed` webhook (`error_type: "ecw_validation_failure"`). * **location\_id** (string, optional): EMR location ID of the facility being referred to. Validated **synchronously** against the account's synced locations — an unknown location returns a `404` and the referral is not queued. Resolve IDs with [`GET /v1/locations`](/api-reference/locations/get) (use the location's `emr_id`). ### Direction and Provider Validation The two provider IDs are validated against different directories depending on the referral's `direction`: | Direction | `referring_from_provider_ehr_id` validated against | `referring_to_provider_ehr_id` validated against | | - | - | - | | `incoming` | Referring providers directory | Providers directory | | `outgoing` | Providers directory | Referring providers directory | Pass `0` for a provider ID when there is no provider on that side. Providers must already be synced — use `GET /v1/providers` and `GET /v1/referring-providers` to resolve IDs. ### Attaching Documents Documents are optional and supplied inline as base64. Each document is an object: ```json theme={null} { "filename": "consult-note.pdf", "content_base64": "JVBERi0xLjQKJ..." } ``` * **filename** must include a file extension (e.g. `report.pdf`). * **content\_base64** must be non-empty, valid base64. Documents are attached to the referral in the EMR **after** the referral itself is created. Because the referral already exists at that point, attachment failures are non-fatal: the referral is still created and the webhook reports how many attachments succeeded and failed. Requests carrying inline documents can be large. The `POST /v1/referrals` endpoint accepts request bodies up to 25 MB. ### Example Request ```bash theme={null} curl -X POST https://api.usecobalt.com/v1/referrals \ -H 'Content-Type: application/json' \ -H 'client_id: ci_live_198908HJDKJSH98789OHKJL' \ -H 'client_secret: cs_live_9827hofdsklOYYHJLJh' \ -H 'access_token: 493JKLHIU98789hLKH9HHJH' \ -d '{ "patient_mrn": "1234567", "referring_from_provider_ehr_id": "PROV-001", "referring_to_provider_ehr_id": "PROV-002", "direction": "outgoing", "priority": "urgent", "reason": "Cardiology consultation", "diagnosis_codes": ["I10", "Z87.39"] }' ``` #### Request with Documents ```bash theme={null} curl -X POST https://api.usecobalt.com/v1/referrals \ -H 'Content-Type: application/json' \ -H 'client_id: ci_live_198908HJDKJSH98789OHKJL' \ -H 'client_secret: cs_live_9827hofdsklOYYHJLJh' \ -H 'access_token: 493JKLHIU98789hLKH9HHJH' \ -d '{ "patient_mrn": "1234567", "referring_from_provider_ehr_id": "PROV-001", "referring_to_provider_ehr_id": "PROV-002", "direction": "outgoing", "priority": "routine", "reason": "Dermatology referral", "documents": [ { "filename": "consult-note.pdf", "content_base64": "JVBERi0xLjQKJ..." } ] }' ``` ### Example Response ```json theme={null} { "success": true, "message": "Referral queued for creation. A webhook event will be sent upon completion.", "referral_id": "123e4567e89b12d3a456426614174000", "job_id": 12345 } ``` The response includes: * **referral\_id**: Cobalt referral ID (UUID without dashes). This is echoed back in the webhook payload so you can correlate the two. * **job\_id**: Job execution identifier for tracking the async operation. ### Error Responses #### Missing Required Field ```json theme={null} { "success": false, "message": "Missing required fields: patient_mrn, reason." } ``` #### Invalid Priority ```json theme={null} { "success": false, "message": "Invalid priority. Must be one of: routine, urgent, stat." } ``` #### Invalid Direction ```json theme={null} { "success": false, "message": "Invalid direction. Must be one of: incoming, outgoing." } ``` #### Invalid Document ```json theme={null} { "success": false, "message": "documents[0].filename must include a file extension (e.g. \"report.pdf\")." } ``` #### Patient Not Found ```json theme={null} { "success": false, "message": "Patient with MRN '1234567' not found. Ensure the patient has been synced." } ``` #### Unknown Provider ```json theme={null} { "success": false, "message": "Unknown provider(s): referring_to_provider_ehr_id 'PROV-002' was not found in referring providers. Ensure the provider has been synced." } ``` #### Unknown Location Returned when `location_id` does not match a synced location for the account. ```json theme={null} { "success": false, "message": "Location with EMR ID '99999' not found. Ensure the location has been synced." } ``` #### User Not Found ```json theme={null} { "success": false, "message": "User not found." } ``` This indicates an issue with your access token. ### Webhook Notifications When the referral has finished processing, we send a webhook to your registered endpoint. #### Success ```json theme={null} { "id": "", "access_token_reference_id": "", "object": "event", "created": "2026-03-10T10:30:00.000Z", "type": "referral.created", "job_id": "12345", "data": { "referral_id": "123e4567e89b12d3a456426614174000", "ehr_id": "REF-00123", "patient_mrn": "1234567", "from_provider_ehr_id": "PROV-001", "to_provider_ehr_id": "PROV-002", "direction": "outgoing", "documents_attached": 1, "documents_failed": 0 } } ``` Note: `documents_attached` and `documents_failed` are only included when the request contained documents. #### Failure ```json theme={null} { "id": "", "access_token_reference_id": "", "object": "event", "created": "2026-03-10T10:35:00.000Z", "type": "referral.failed", "job_id": "12345", "data": { "referral_id": "123e4567e89b12d3a456426614174000", "patient_mrn": "1234567", "failure_reason": "Unable to create referral in eCW. ECW rejected the request or returned an unexpected response." } } ``` # Get Referrals Source: https://docs.usecobalt.com/api-reference/referrals/get GET /referrals Retrieve referrals from the Cobalt database for a specified date range. ## Query Parameters * **start\_date** (string, required): Start of the date range in ISO format (YYYY-MM-DD) * **end\_date** (string, required): End of the date range in ISO format (YYYY-MM-DD) * **patient\_mrn** (string, optional): Filter by patient MRN * **status** (string, optional): Filter by referral status * **priority** (string, optional): Filter by priority — `routine`, `urgent`, or `stat` * **direction** (string, optional): Filter by referral direction — `incoming` or `outgoing` * **ehr\_id** (string, optional): Filter by the referral's EMR ID * **receiving\_line** (string, optional): Filter by the receiving line — the fax number that received the document. Accepts any format; the value is canonicalized to E.164 before matching. * **page** (integer, optional): Page number (default: 1) * **page\_size** (integer, optional): Results per page, max 100 (default: 100) * **sort** (string, optional): Sort order — pass `created_at` for ascending, defaults to descending by referral date ## Example Request ```bash theme={null} curl -X GET "https://api.usecobalt.com/v1/referrals?start_date=2026-03-01&end_date=2026-03-31" \ -H "client_id: your_client_id" \ -H "client_secret: your_client_secret" \ -H "access_token: your_access_token" ``` ## Example Response ```json theme={null} { "success": true, "referrals": [ { "id": "a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4", "ehr_id": "REF-00123", "patient_ehr_id": "PAT-456", "patient_mrn": "1234567", "patient_first_name": "Jane", "patient_last_name": "Smith", "patient_dob": "1985-03-15", "from_provider_ehr_id": "PROV-001", "from_provider_name": "Dr. John Doe", "to_provider_ehr_id": "PROV-002", "to_provider_name": "Dr. Sarah Lee", "priority": "urgent", "urgency": "urgent", "reason": "Cardiology consultation", "prior_authorization_code": "AUTH-12345", "specialty": "Cardiology", "to_facility_name": "Example Regional Hospital", "diagnosis_codes": ["I10", "Z87.39"], "document_ids": ["b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5"], "direction": "incoming", "receiving_line": "+12345678901", "status": "pending", "referral_date": "2026-03-10T00:00:00.000Z", "last_activity_at": "2026-03-11T14:30:00.000Z", "synced_at": "2026-03-11T14:35:00.000Z" } ], "pagination": { "page": 1, "page_size": 100, "total_count": 1, "total_pages": 1 } } ``` ## Response Fields ### Top-Level Fields * **success** (boolean): Whether the request was successful * **referrals** (array): Array of referral objects * **pagination** (object): Pagination metadata ### Referral Object * **id** (string): Cobalt referral ID (UUID without dashes) * **ehr\_id** (string): The referral ID as it appears in the EMR * **patient\_ehr\_id** (string, nullable): The patient's EMR ID * **patient\_mrn** (string, nullable): Patient medical record number * **patient\_first\_name** (string, nullable): Patient first name * **patient\_last\_name** (string, nullable): Patient last name * **patient\_dob** (string, nullable): Patient date of birth * **from\_provider\_ehr\_id** (string, nullable): EMR ID of the referring provider * **from\_provider\_name** (string, nullable): Name of the referring provider * **to\_provider\_ehr\_id** (string, nullable): EMR ID of the provider being referred to * **to\_provider\_name** (string, nullable): Name of the provider being referred to * **priority** (string, nullable): Referral priority — `routine`, `urgent`, or `stat` * **urgency** (string, nullable): Urgency designation as recorded in the EMR * **reason** (string, nullable): Reason for the referral * **prior\_authorization\_code** (string, nullable): Prior authorization code recorded on the referral * **specialty** (string, nullable): Referral specialty * **to\_facility\_name** (string, nullable): Name of the facility the referral is directed to * **diagnosis\_codes** (array): ICD-10 diagnosis codes associated with the referral * **document\_ids** (array): Cobalt IDs of attached documents (UUIDs without dashes) * **direction** (string, nullable): Referral direction — `incoming` or `outgoing` * **receiving\_line** (string, nullable): The receiving line — the fax number that received the document — in E.164 format (e.g. `+12345678901`). * **status** (string, nullable): Current referral status * **referral\_date** (string, nullable): Date the referral was created (ISO 8601) * **last\_activity\_at** (string, nullable): Timestamp of the most recent activity on the referral (ISO 8601) * **synced\_at** (string, nullable): Timestamp of the last sync from the EMR (ISO 8601) ## Notes * This endpoint returns referrals from the Cobalt database. Data is updated during scheduled syncs. # Update Referral Source: https://docs.usecobalt.com/api-reference/referrals/update PATCH /referrals/{id} Queues an update to an existing referral in the provider's EMR system. Referrals are updated in the provider's EMR using the same asynchronous processing model as [creation](/api-reference/referrals/create). The update is not instantaneous. Instead of leaving your PATCH request hanging until completion, we immediately return a success response if the request is properly formed. We then notify you via a webhook when the update has been applied in the EMR. ### Path Parameter * **id** (string, required): The Cobalt referral ID (UUID) returned by [Create Referral](/api-reference/referrals/create) or [Get Referrals](/api-reference/referrals/get). The referral must already exist and have been created in the EMR. ### Request Parameters Supply **one or more** of the following fields. Omitted fields are left unchanged in the EMR — this is a partial update. * **priority** (string, optional): Referral priority — `routine`, `urgent`, or `stat`. Case-insensitive: `"Routine"`, `"URGENT"`, and `"stat"` are all accepted and normalized. * **status** (string, optional): Referral status label — e.g. `"Open"`, `"Pending"`, `"Internal Review"`, `"Insurance Auth"`, `"Addressed"`. See [Status Values](#status-values) below. * **reason** (string, optional): Reason for the referral. * **notes** (string, optional): Free-text notes on the referral. * **prior\_authorization\_code** (string, optional): Prior authorization code to write on the referral. * **specialty** (string, optional): Referral specialty name — e.g. `"Cardiology"`. See [Specialty](#specialty) below. * **location\_id** (string, optional): EMR location ID of the facility the referral is directed to. See [To Facility](#to-facility) below. At least one updatable field must be present. An empty body returns a `400`. ### Status Values The set of valid statuses — and their exact labels — is **specific to each EMR instance and organization**, not a fixed global list. When you PATCH a `status`, the value is resolved against that instance's live status list at processing time. Because of this, you can pass a human-friendly, title-cased label with spaces (e.g. `"Internal Review"`) regardless of how the status is stored internally in the EMR. Matching is case- and spacing-insensitive. If the supplied status does not match any status configured for the instance, the update fails and a `referral.failed` webhook is sent with `error_type: "ecw_validation_failure"`. To discover which statuses an instance supports, inspect the `status` field returned by [Get Referrals](/api-reference/referrals/get) for existing referrals. ### Specialty Pass **specialty** as the human-friendly specialty name (e.g. `"Cardiology"`, `"Sport Medicine"`). The name is resolved against the EMR instance's live specialty list at processing time, so the exact set of valid names is instance/organization specific. If the supplied specialty does not match any specialty configured for the instance, the update fails and a `referral.failed` webhook is sent with `error_type: "ecw_validation_failure"`. ### To Facility Pass **location\_id** as the EMR location ID of the facility the referral is directed to. It is validated **synchronously** against the account's synced locations — an unknown location returns a `404` and no update is queued. Resolve location IDs with [`GET /v1/locations`](/api-reference/locations/get); use the location's `emr_id` value. ### Example Request ```bash theme={null} curl -X PATCH https://api.usecobalt.com/v1/referrals/123e4567e89b12d3a456426614174000 \ -H 'Content-Type: application/json' \ -H 'client_id: ci_live_198908HJDKJSH98789OHKJL' \ -H 'client_secret: cs_live_9827hofdsklOYYHJLJh' \ -H 'access_token: 493JKLHIU98789hLKH9HHJH' \ -d '{ "priority": "urgent", "status": "Internal Review", "reason": "Updated cardiology consultation", "notes": "Patient called to confirm availability.", "prior_authorization_code": "AUTH-12345", "specialty": "Cardiology", "location_id": "loc-123" }' ``` #### Update a Single Field ```bash theme={null} curl -X PATCH https://api.usecobalt.com/v1/referrals/123e4567e89b12d3a456426614174000 \ -H 'Content-Type: application/json' \ -H 'client_id: ci_live_198908HJDKJSH98789OHKJL' \ -H 'client_secret: cs_live_9827hofdsklOYYHJLJh' \ -H 'access_token: 493JKLHIU98789hLKH9HHJH' \ -d '{ "status": "Addressed" }' ``` ### Example Response ```json theme={null} { "success": true, "message": "Referral queued for update. A webhook event will be sent upon completion.", "referral_id": "123e4567e89b12d3a456426614174000", "job_id": 12345 } ``` The response includes: * **referral\_id**: Cobalt referral ID (UUID without dashes). Echoed back in the webhook payload so you can correlate the two. * **job\_id**: Job execution identifier for tracking the async operation. ### Error Responses #### Invalid Priority ```json theme={null} { "success": false, "message": "Invalid priority. Must be one of: routine, urgent, stat (case-insensitive)." } ``` #### Invalid Status ```json theme={null} { "success": false, "message": "Invalid status. Must be a non-empty string." } ``` #### No Updatable Fields ```json theme={null} { "success": false, "message": "No updatable fields provided. Supply at least one of: priority, status, reason, notes, prior_authorization_code, specialty, location_id." } ``` #### Unexpected Fields ```json theme={null} { "success": false, "message": "Unexpected fields in request body: foo. Updatable fields are: priority, status, reason, notes, prior_authorization_code, specialty, location_id." } ``` #### Unknown Location Returned when `location_id` does not match a synced location for the account. Validated synchronously, so no update is queued. ```json theme={null} { "success": false, "message": "Location with EMR ID '99999' not found. Ensure the location has been synced." } ``` #### Invalid Referral ID ```json theme={null} { "success": false, "message": "Invalid referral ID format. Must be a valid UUID." } ``` #### Referral Not Yet in the EMR ```json theme={null} { "success": false, "message": "Referral has not been created in the EMR yet and cannot be updated." } ``` #### Referral Not Found ```json theme={null} { "success": false, "message": "Referral not found." } ``` ### Webhook Notifications When the update has finished processing, we send a webhook to your registered endpoint. #### Success ```json theme={null} { "id": "", "access_token_reference_id": "", "object": "event", "created": "2026-03-10T10:30:00.000Z", "type": "referral.updated", "job_id": "12345", "data": { "referral_id": "123e4567e89b12d3a456426614174000", "ehr_id": "REF-00123", "patient_mrn": "1234567", "direction": "outgoing", "updated_fields": ["priority", "status", "specialty", "location_id"] } } ``` `updated_fields` lists the fields that were applied in this update. #### Failure ```json theme={null} { "id": "", "access_token_reference_id": "", "object": "event", "created": "2026-03-10T10:35:00.000Z", "type": "referral.failed", "job_id": "12345", "data": { "referral_id": "123e4567e89b12d3a456426614174000", "patient_mrn": "1234567", "failure_reason": "The status \"Foo\" is not a valid status for this eCW instance.", "error_type": "ecw_validation_failure" } } ``` `error_type` is only present for validation failures (e.g. an unrecognized status or specialty). Other failures omit it. # Create Referring Provider Source: https://docs.usecobalt.com/api-reference/referring-providers/create POST /referring-providers Creates a new referring provider record in your EMR system. ### Request Parameters #### Required Fields * **first\_name** (string, required): Provider's first name * **last\_name** (string, required): Provider's last name * **npi** (string, required): National Provider Identifier #### Optional Fields * **phone** (string): Contact phone number in XXX-XXX-XXXX format * **fax** (string): Fax number in XXX-XXX-XXXX format * **email** (string): Contact email address * **address** (string): Street address * **city** (string): City name * **state** (string): State abbreviation (e.g., "IL", "CA") * **zip\_code** (string): ZIP code ### Phone and Fax Format Phone and fax numbers must be provided in **XXX-XXX-XXXX** format. If the format is incorrect, you will receive a 400 error. ```json theme={null} // Valid "phone": "312-555-1234" // Invalid "phone": "3125551234" "phone": "(312) 555-1234" ``` ### Example Request ```bash theme={null} curl -X POST https://api.usecobalt.com/v1/referring-providers \ -H 'Content-Type: application/json' \ -H 'client_id: ci_live_198908HJDKJSH98789OHKJL' \ -H 'client_secret: cs_live_9827hofdsklOYYHJLJh' \ -H 'access_token: 493JKLHIU98789hLKH9HHJH' \ -d '{ "first_name": "John", "last_name": "Smith", "npi": "1234567890", "phone": "312-555-1234", "fax": "312-555-5678", "email": "john.smith@example.com", "address": "123 Main St", "city": "Chicago", "state": "IL", "zip_code": "60601" }' ``` ### Example Response ```json theme={null} { "success": true, "message": "Referring provider processing. A webhook event will be sent upon completion.", "referring_provider_id": "e35e82b85df58f08a9b38e06a91e9f1a", "job_id": 12345 } ``` The response includes: * **referring\_provider\_id**: Unique identifier for the created referring provider record (UUID without dashes) * **job\_id**: Job execution identifier for tracking the async operation ### Error Responses #### Missing Required Field ```json theme={null} { "success": false, "message": "Missing required fields: npi." } ``` #### Invalid Phone Format ```json theme={null} { "success": false, "message": "Phone number must be in XXX-XXX-XXXX format." } ``` #### Invalid Fax Format ```json theme={null} { "success": false, "message": "Fax number must be in XXX-XXX-XXXX format." } ``` ### Webhook Notifications When the referring provider processing is complete, we will send a webhook to your registered endpoint. Here are examples of what those webhook payloads will look like: #### Success ```json theme={null} { "id": "evt_1J9X2q2eZvKYlo2CluR9g9gV", "access_token_reference_id": "user_1J9X2q2eZvKYlo2Cxyz", "object": "event", "created": "2025-01-18T10:30:00Z", "type": "referring_provider.created", "job_id": "12345", "data": { "referring_provider_id": "e35e82b85df58f08a9b38e06a91e9f1a", "ehr_id": "350143", "first_name": "John", "last_name": "Smith", "npi": "1234567890" } } ``` #### Failure - Invalid NPI ```json theme={null} { "id": "evt_1J9X2q2eZvKYlo2CluR9g9gW", "access_token_reference_id": "user_1J9X2q2eZvKYlo2Cxyz", "object": "event", "created": "2025-01-18T10:35:00Z", "type": "referring_provider.failed", "job_id": "12345", "data": { "referring_provider_id": "e35e82b85df58f08a9b38e06a91e9f1a", "first_name": "John", "last_name": "Smith", "npi": "1234567890", "failure_reason": "Invalid NPI: 1234567890. The NPI number is not valid.", "error_type": "invalid_npi" } } ``` #### Failure - EMR Validation Error ```json theme={null} { "id": "evt_1J9X2q2eZvKYlo2CluR9g9gX", "access_token_reference_id": "user_1J9X2q2eZvKYlo2Cxyz", "object": "event", "created": "2025-01-18T10:40:00Z", "type": "referring_provider.failed", "job_id": "12345", "data": { "referring_provider_id": "e35e82b85df58f08a9b38e06a91e9f1a", "first_name": "John", "last_name": "Smith", "npi": "1234567890", "failure_reason": "Unable to create referring provider in eCW. ECW rejected the request. Please verify all required fields are provided and the provider doesn't already exist.", "error_type": "ecw_validation_failure" } } ``` #### Failure - Other Errors ```json theme={null} { "id": "evt_1J9X2q2eZvKYlo2CluR9g9gY", "access_token_reference_id": "user_1J9X2q2eZvKYlo2Cxyz", "object": "event", "created": "2025-01-18T10:45:00Z", "type": "referring_provider.failed", "job_id": "12345", "data": { "referring_provider_id": "e35e82b85df58f08a9b38e06a91e9f1a", "first_name": "John", "last_name": "Smith", "npi": "1234567890", "failure_reason": "Failed to create referring provider" } } ``` Referring provider creation is asynchronous. Store the returned referring\_provider\_id and listen for webhooks to determine the final status. ## Usage with Patient Creation Once created, you can use the `referring_provider_id` when creating patients instead of providing referring provider names. See the [Create Patient](/api-reference/patients/create) documentation for details on using `referring_provider_id`. ## Notes * NPI validation is performed by the EMR system * Some EMRs may reject duplicate providers with the same NPI * Phone and fax numbers are normalized before being sent to the EMR * The `ehr_id` returned in the success webhook is the provider's ID in your EMR system # Get Referring Providers Source: https://docs.usecobalt.com/api-reference/referring-providers/get GET /referring-providers Retrieve referring providers from the EMR system for your organization. ## Query Parameters All parameters are optional and can be combined to filter results: * **first\_name** (string): Filter by first name (case-insensitive, partial match) * **last\_name** (string): Filter by last name (case-insensitive, partial match) * **phone** (string): Filter by phone number (accepts any format, e.g., "7734722936" or "773-472-2936") * **fax** (string): Filter by fax number (accepts any format) * **npi** (string): Filter by NPI (exact match) * **emr\_referring\_provider\_id** (string): Filter by the referring provider's EMR ID (exact match). Use this to resolve a referring provider's Cobalt `id` (for `pcp_id`) from the EMR ID you already hold. ## Example Requests ### Get All Referring Providers ```bash theme={null} curl -X GET "https://api.usecobalt.com/v1/referring-providers" \ -H "client_id: your_client_id" \ -H "client_secret: your_client_secret" \ -H "access_token: your_access_token" ``` ### Filter by Last Name ```bash theme={null} curl -X GET "https://api.usecobalt.com/v1/referring-providers?last_name=smith" \ -H "client_id: your_client_id" \ -H "client_secret: your_client_secret" \ -H "access_token: your_access_token" ``` ### Filter by NPI ```bash theme={null} curl -X GET "https://api.usecobalt.com/v1/referring-providers?npi=1780690362" \ -H "client_id: your_client_id" \ -H "client_secret: your_client_secret" \ -H "access_token: your_access_token" ``` ### Filter by Phone ```bash theme={null} curl -X GET "https://api.usecobalt.com/v1/referring-providers?phone=773-472-2936" \ -H "client_id: your_client_id" \ -H "client_secret: your_client_secret" \ -H "access_token: your_access_token" ``` ### Resolve Cobalt id from EMR ID (for pcp\_id) ```bash theme={null} curl -X GET "https://api.usecobalt.com/v1/referring-providers?emr_referring_provider_id=350143" \ -H "client_id: your_client_id" \ -H "client_secret: your_client_secret" \ -H "access_token: your_access_token" ``` ## Example Response ```json theme={null} { "success": true, "referring_providers": [ { "id": "e35e82b85df58f08a9b38e06a91e9f1a", "ehr_id": "350143", "first_name": "jane", "last_name": "Doe", "middle_initial": null, "speciality": "Primary Care Doctor", "phone": "222-333-4444", "fax": "222-333-4444", "email": null, "npi": "98374982374", "address_street": "3336 N Main AVE", "address_city": "Main", "address_state": "IL", "address_zip": "22333" }, ] } ``` ## Response Fields ### Top-Level Fields * **success** (boolean): Whether the request was successful * **referring\_providers** (array): Array of referring provider objects for the organization ### Referring Provider Object * **id** (string): Cobalt referring provider ID (UUID without dashes) * **ehr\_id** (string): The referring provider ID as it appears in the EMR system * **name** (string): Full name (typically formatted as "LASTNAME,FIRSTNAME") * **first\_name** (string): Provider's first name * **last\_name** (string): Provider's last name * **middle\_initial** (string, nullable): Provider's middle initial * **speciality** (string, nullable): Medical speciality (e.g., "Cardiology", "Primary Care Doctor") * **phone** (string, nullable): Contact phone number in format XXX-XXX-XXXX * **fax** (string, nullable): Fax number in format XXX-XXX-XXXX * **email** (string, nullable): Contact email address * **npi** (string, nullable): National Provider Identifier * **address\_street** (string, nullable): Street address * **address\_city** (string, nullable): City name * **address\_state** (string, nullable): State abbreviation * **address\_zip** (string, nullable): ZIP code ## Notes * All referring providers returned are active in the system * Phone and fax numbers are normalized to XXX-XXX-XXXX format * Search filters are applied as partial matches (except NPI which is exact) * Results are ordered by last name, then first name alphabetically # Get Settings Source: https://docs.usecobalt.com/api-reference/settings/get GET /settings Returns the current settings for the associated access token. ### Example Request ```bash theme={null} curl -X GET https://api.usecobalt.com/v1/settings \ -H 'Content-Type: application/json' \ -H 'client_id: ci_live_198908HJDKJSH98789OHKJL' \ -H 'client_secret: cs_live_9827hofdsklOYYHJLJh' \ -H 'access_token: 493JKLHIU98789hLKH9HHJH' ``` ### Example Response ```json theme={null} { "success": true, "data": { "access_token_reference_id": "at_ref_123456789", "visit_types": [ { "code": "new_patient", "description": "New Patient Visit (in-person)" }, { "code": "televisit1", "description": "televisit1 (televisit / osa)" } ], "visit_statuses": [ { "code": "CANC", "description": "Cancelled in advance" }, { "code": "R/S", "description": "Rescheduled" } ], "required_patient_fields": [ "first_name", "last_name", "dob", "sex" ], "timezone": "America/New_York", "staff_list": [ { "ehr_id": "12220", "name": "Smith,Jane" }, { "ehr_id": "12241", "name": "Doe,John" } ], "departments": [ { "ehr_id": "1", "name": "MEDICAL", "type": "MED", "location_ehr_id": "12" }, { "ehr_id": "2", "name": "BEHAVIORAL HEALTH", "type": "MED", "location_ehr_id": "12" }, { "ehr_id": "5", "name": "RADIOLOGY", "type": "MED", "location_ehr_id": "17" } ] } } ``` Settings include the access token reference ID, available visit types (with codes and descriptions), and the user's timezone. If settings are not found for the user, a 404 error will be returned. # Update Settings Source: https://docs.usecobalt.com/api-reference/settings/update POST /settings Updates the settings for the associated access token. ### Example Request ```bash theme={null} curl -X POST https://api.usecobalt.com/v1/settings \ -H 'Content-Type: application/json' \ -H 'client_id: ci_live_198908HJDKJSH98789OHKJL' \ -H 'client_secret: cs_live_9827hofdsklOYYHJLJh' \ -H 'access_token: 493JKLHIU98789hLKH9HHJH' \ -d '{ "access_token_reference_id": "at_ref_123456789" }' ``` ### Example Response ```json theme={null} { "success": true, "data": { "access_token_reference_id": "at_ref_123456789" } } ``` The `access_token_reference_id` is required in the request body. No other fields are accepted. # Get Staff Source: https://docs.usecobalt.com/api-reference/staff/get GET /staff Returns the staff synced for the account. ### Example Request ```bash theme={null} curl -X GET https://api.usecobalt.com/v1/staff \ -H 'Content-Type: application/json' \ -H 'client_id: ci_live_198908HJDKJSH98789OHKJL' \ -H 'client_secret: cs_live_9827hofdsklOYYHJLJh' \ -H 'access_token: 493JKLHIU98789hLKH9HHJH' ``` ### Example Response ```json theme={null} { "success": true, "data": [ { "ehr_id": "12220", "name": "Smith,Jane" }, { "ehr_id": "12241", "name": "Doe,John" } ] } ``` Use an entry's `ehr_id` wherever a request takes a staff ID, such as `assigned_to_id`. # Get Last Schedule Sync Source: https://docs.usecobalt.com/api-reference/system/last-schedule-sync GET /last-schedule-sync Retrieves the timestamp of the last successful schedule synchronization. ### Example Request ```bash theme={null} curl -X GET https://api.usecobalt.com/v1/last-schedule-sync \ -H 'Content-Type: application/json' \ -H 'client_id: ci_live_198908HJDKJSH98789OHKJL' \ -H 'client_secret: cs_live_9827hofdsklOYYHJLJh' \ -H 'access_token: 493JKLHIU98789hLKH9HHJH' ``` ### Example Response ```json theme={null} { "success": true, "last_sync": { "timestamp": "2025-01-24T15:11:05.117-06:00" } } ``` # Create Task Source: https://docs.usecobalt.com/api-reference/tasks/create POST /tasks Creates a task in the provider's EMR system. Depending on the EMR system connected to the account, additional fields beyond the base required fields may be required. If a conditionally required field is missing, the API will return a `400` error specifying the missing field. ### Request Parameters #### Required Fields These fields are always required regardless of the connected EMR: * **description** (string, required): Task description/body content * **patient\_mrn** (string, required): Medical Record Number (MRN) of the patient #### Conditionally Required Fields Depending on the connected EMR, some combination of the following fields will also be required: * **subject** (string, conditionally required): Task title/subject line * **assignee\_user\_id** (string, conditionally required): EMR user ID to assign the task to * **phone\_call\_type\_id** (string, conditionally required): Identifier for the phone call type * **notify\_list** (string\[], optional): List of user identifiers to notify about the task. Must be an array of strings if provided. ### Example Requests ```bash theme={null} curl -X POST https://api.usecobalt.com/v1/tasks \ -H 'Content-Type: application/json' \ -H 'client_id: ci_live_198908HJDKJSH98789OHKJL' \ -H 'client_secret: cs_live_9827hofdsklOYYHJLJh' \ -H 'access_token: 493JKLHIU98789hLKH9HHJH' \ -d '{ "description": "Schedule patient follow-up appointment to review recent lab results and discuss treatment plan.", "patient_mrn": "MRN-12345", "subject": "Follow-up appointment needed", "assignee_user_id": "74196090-5faa-11e9-9562-e12f6a3ce827" }' ``` ```bash theme={null} curl -X POST https://api.usecobalt.com/v1/tasks \ -H 'Content-Type: application/json' \ -H 'client_id: ci_live_198908HJDKJSH98789OHKJL' \ -H 'client_secret: cs_live_9827hofdsklOYYHJLJh' \ -H 'access_token: 493JKLHIU98789hLKH9HHJH' \ -d '{ "description": "Patient called requesting prescription refill for lisinopril 10mg.", "patient_mrn": "MRN-67890", "phone_call_type_id": "rx_refill", "notify_list": ["user_001", "user_002"] }' ``` ```bash theme={null} curl -X POST https://api.usecobalt.com/v1/tasks \ -H 'Content-Type: application/json' \ -H 'client_id: ci_live_198908HJDKJSH98789OHKJL' \ -H 'client_secret: cs_live_9827hofdsklOYYHJLJh' \ -H 'access_token: 493JKLHIU98789hLKH9HHJH' \ -d '{ "description": "Patient reports side effects from new medication; please review.", "patient_mrn": "MRN-24680", "task_type": "Prescribe", "details": "Medication", "assigned_to": "4840", "facility_id": "5954", "due_date": "2026-11-25" }' ``` ```bash theme={null} curl -X POST https://api.usecobalt.com/v1/tasks \ -H 'Content-Type: application/json' \ -H 'client_id: ci_live_198908HJDKJSH98789OHKJL' \ -H 'client_secret: cs_live_9827hofdsklOYYHJLJh' \ -H 'access_token: 493JKLHIU98789hLKH9HHJH' \ -d '{ "patient_mrn": "MRN-13579", "subject": "Add pharmacy", "assigned_to": "152925", "description": "Confirm preferred pharmacy on file.", "due_date": "2026-11-25", "start_date": "2026-11-18", "priority": "Normal" }' ``` ### Example Response ```json theme={null} { "success": true, "message": "Task processing. A webhook event will be sent upon completion.", "task_id": "abc123def456789012345678", "job_id": 12345 } ``` The response includes: * **task\_id**: Unique identifier for the created task record * **job\_id**: Job execution identifier for tracking the async operation ### Error Responses #### Missing Required Field If a required or conditionally required field is missing, the API returns a `400` with the specific field name: ```json theme={null} { "success": false, "message": "Missing required field: description" } ``` #### User Not Found ```json theme={null} { "success": false, "message": "User not found." } ``` ### Webhook Notifications When the task processing is complete, we will send a webhook to your registered endpoint. Here are examples of what those webhook payloads will look like: #### Success ```json theme={null} { "id": "evt_1J9X2q2eZvKYlo2CluR9g9gV", "access_token_reference_id": "user_1J9X2q2eZvKYlo2Cxyz", "object": "event", "created": "2025-10-09T10:30:00Z", "type": "task.created", "job_id": "12345", "data": { "task_id": "abc123def456789012345678", "emr_task_id": "task_emr_internal_id", "subject": "Follow-up appointment needed", "description": "Schedule patient follow-up appointment to review recent lab results and discuss treatment plan.", "assignee_user_id": "74196090-5faa-11e9-9562-e12f6a3ce827", "patient_mrn": "MRN-12345" } } ``` #### Failure ```json theme={null} { "id": "evt_1J9X2q2eZvKYlo2CluR9g9gW", "access_token_reference_id": "user_1J9X2q2eZvKYlo2Cxyz", "object": "event", "created": "2025-10-09T10:35:00Z", "type": "task.failed", "job_id": "12345", "data": { "task_id": "abc123def456789012345678", "failure_reason": "Failed to create task" } } ``` # Create Task Note Source: https://docs.usecobalt.com/api-reference/tasks/notes/create POST /tasks/notes Appends a note to an existing task in the provider's EMR system. This endpoint appends a note to a task that already exists in the EMR. To find a task's `emr_task_id`, perform a live fetch with `POST /v1/patients/fetch` and `include: ["tasks"]` — each task in the response carries its `emr_task_id`. ### Request Parameters * **emr\_task\_id** (string, required): The EMR ID of the task to append the note to. Obtain this from the `tasks[]` array returned by `POST /v1/patients/fetch` with `include: ["tasks"]`. * **note** (string, required): The note body content. ### Example Request ```bash theme={null} curl -X POST https://api.usecobalt.com/v1/tasks/notes \ -H 'Content-Type: application/json' \ -H 'client_id: ci_live_198908HJDKJSH98789OHKJL' \ -H 'client_secret: cs_live_9827hofdsklOYYHJLJh' \ -H 'access_token: 493JKLHIU98789hLKH9HHJH' \ -d '{ "emr_task_id": "9bef4113-e9ce-4895-9a17-2c18cd625116", "note": "Patient called back, scheduled for next week." }' ``` ### Example Response ```json theme={null} { "success": true, "message": "Task note processing. A webhook event will be sent upon completion.", "task_note_id": "abc123def456789012345678", "job_id": 12345 } ``` The response includes: * **task\_note\_id**: Unique identifier for the created task note record * **job\_id**: Job execution identifier for tracking the async operation ### Error Responses #### Missing Required Field ```json theme={null} { "success": false, "message": "Missing required field: emr_task_id" } ``` #### Unexpected Field Only `emr_task_id` and `note` are accepted. Any other field returns a `400`: ```json theme={null} { "success": false, "message": "Unexpected fields in request body: subject" } ``` ### Webhook Notifications When the task note processing is complete, we will send a webhook to your registered endpoint. #### Success ```json theme={null} { "id": "evt_1J9X2q2eZvKYlo2CluR9g9gV", "access_token_reference_id": "user_1J9X2q2eZvKYlo2Cxyz", "object": "event", "created": "2026-06-15T12:02:40Z", "type": "task_note.created", "job_id": "12345", "data": { "task_note_id": "abc123def456789012345678", "emr_note_id": "f862b0a0-d820-418c-9e30-412bb62bde34", "emr_task_id": "9bef4113-e9ce-4895-9a17-2c18cd625116", "note": "Patient called back, scheduled for next week." } } ``` #### Failure ```json theme={null} { "id": "evt_1J9X2q2eZvKYlo2CluR9g9gW", "access_token_reference_id": "user_1J9X2q2eZvKYlo2Cxyz", "object": "event", "created": "2026-06-15T12:05:00Z", "type": "task_note.failed", "job_id": "12345", "data": { "task_note_id": "abc123def456789012345678", "failure_reason": "Failed to create task note" } } ``` # Create Telephone Encounter Source: https://docs.usecobalt.com/api-reference/telephone-encounters/create POST /telephone-encounters Creates a telephone encounter record in the provider's EMR system. Telephone encounters are created in the provider's EMR using an asynchronous processing model. Creating telephone encounters this way is not instantaneous. Instead of leaving your POST request hanging until completion, we immediately return a success response if the request is properly formed. We then notify you via a webhook when the telephone encounter processing has completed. This gives you flexibility around your user experience. For example, when you first make the `/telephone-encounters` call you can display a **Processing** status to your user and when you get the webhook notification you can update that to **Completed**. ### Request Parameters #### Required Fields * **patient\_mrn** (string, required): Patient's Medical Record Number * **provider\_id** (string, required): Provider's EMR ID * **location\_id** (string, required): Location's EMR ID * **assigned\_to\_id** (string, required): EMR user ID of the staff member the encounter is assigned to. This can be any staff member in your organization (provider, nurse, front desk), not just providers. Valid IDs are the `ehr_id` values returned by `GET /v1/staff`. #### Optional Fields * **reason** (string, optional, max 50 characters): Reason for telephone encounter * **refill\_medication\_name** (string, optional, 2-50 characters): Name of medication to refill. If provided, the system will search for a matching medication in the patient's available refillable medications and add it to the encounter. The medication name is matched case-insensitively and supports partial matches. * **pharmacy\_ehr\_id** (string, optional): Pharmacy's EMR ID. If provided, the pharmacy must exist in your organization's pharmacy list (validate via GET /v1/pharmacies). When used with a medication refill, the pharmacy will be associated with the encounter. If the pharmacy is not already in the patient's pharmacy list, it will be added automatically. * **caller** (string, optional, max 100 characters): Name of the person who called * **message** (string, optional): Message content from the caller * **is\_high\_priority** (string, optional): Whether this encounter should be marked as high priority. Must be "true" or "false". * **template\_name** (string, optional): The template name to use when filling out the document upload form. ### Medication Refill Behavior When `refill_medication_name` is provided: 1. **Medication Search**: The system searches the patient's available medications for a match 2. **Filtering**: Only refillable medications that are available for the patient are considered 3. **Matching**: Medication names are matched case-insensitively with partial name support 4. **Success**: If found, the encounter is created and the medication refill is added to it 5. **Failure**: If the medication is not found, the encounter is NOT created and an error is returned with a list of available medications ### Example Request #### Basic Request ```bash theme={null} curl -X POST https://api.usecobalt.com/v1/telephone-encounters \ -H 'Content-Type: application/json' \ -H 'client_id: ci_live_198908HJDKJSH98789OHKJL' \ -H 'client_secret: cs_live_9827hofdsklOYYHJLJh' \ -H 'access_token: 493JKLHIU98789hLKH9HHJH' \ -d '{ "patient_mrn": "MRN-123456", "provider_id": "12241", "location_id": "25", "assigned_to_id": "12220", "reason": "Follow up on test results" }' ``` #### Request with Medication Refill ```bash theme={null} curl -X POST https://api.usecobalt.com/v1/telephone-encounters \ -H 'Content-Type: application/json' \ -H 'client_id: ci_live_198908HJDKJSH98789OHKJL' \ -H 'client_secret: cs_live_9827hofdsklOYYHJLJh' \ -H 'access_token: 493JKLHIU98789hLKH9HHJH' \ -d '{ "patient_mrn": "MRN-123456", "provider_id": "12241", "location_id": "25", "assigned_to_id": "12220", "reason": "Prescription refill", "refill_medication_name": "Lisinopril", "pharmacy_ehr_id": "1643" }' ``` ### Example Response ```json theme={null} { "success": true, "message": "Telephone encounter processing. A webhook event will be sent upon completion.", "telephone_encounter_id": "123e4567e89b12d3a456426614174000", "job_id": 12345 } ``` The response includes: * **telephone\_encounter\_id**: Unique identifier for the created telephone encounter record * **job\_id**: Job execution identifier for tracking the async operation ### Error Responses #### Missing Required Field ```json theme={null} { "success": false, "message": "Missing required field: patient_mrn" } ``` Possible missing fields: `patient_mrn`, `provider_id`, `location_id`, `assigned_to_id` #### Reason Too Long ```json theme={null} { "success": false, "message": "Reason exceeds maximum length of 50 characters" } ``` #### Medication Name Invalid Length ```json theme={null} { "success": false, "message": "refill_medication_name must be at least 2 characters" } ``` ```json theme={null} { "success": false, "message": "refill_medication_name exceeds maximum length of 50 characters" } ``` #### Provider Not Found ```json theme={null} { "success": false, "message": "Provider with EMR ID '99999' not found." } ``` This error occurs when `provider_id` doesn't exist in the providers table. Sync providers using `GET /v1/providers`. #### Staff Member Not Found ```json theme={null} { "success": false, "message": "Staff member with ID '99999' not found." } ``` This error occurs when `assigned_to_id` doesn't exist in the staff list. Valid staff IDs are the `ehr_id` values returned by `GET /v1/staff`. #### Location Not Found ```json theme={null} { "success": false, "message": "Location with EMR ID '99999' not found." } ``` Sync locations using `GET /v1/locations` to resolve this error. #### Pharmacy Not Found ```json theme={null} { "success": false, "message": "Pharmacy with EMR ID '99999' not found." } ``` This error occurs when `pharmacy_ehr_id` is present but no corresponding pharmacy can be found. Call `GET /v1/pharmacies` to see what available pharmacies there are. #### User Not Found ```json theme={null} { "success": false, "message": "User not found." } ``` This indicates an issue with your access token. #### Unsupported EMR ```json theme={null} { "success": false, "message": "Telephone encounters are only supported for [EMR Name] EMR." } ``` Your EMR system may not currently support telephone encounter creation. ### Webhook Notifications When the telephone encounter processing is complete, we will send a webhook to your registered endpoint. Here are examples of what those webhook payloads will look like: #### Success ```json theme={null} { "id": "", "access_token_reference_id": "", "object": "event", "created": "2025-10-27T10:30:00Z", "type": "telephone_encounter.created", "job_id": "12345", "data": { "telephone_encounter_id": "123e4567e89b12d3a456426614174000", "emr_encounter_id": "ECW-98765", "patient_mrn": "MRN-123456", "provider_id": "12241", "location_id": "25", "assigned_to_id": "12220", "reason": "Follow up on test results", "refill_medication_name": "Lisinopril 10 MG" } } ``` Note: `refill_medication_name` will be included in the webhook data if a medication refill was requested and successfully added. #### Partial Success When the encounter is created successfully but the medication refill fails to be added, a partial success webhook is sent: ```json theme={null} { "id": "", "access_token_reference_id": "", "object": "event", "created": "2025-10-27T10:35:00Z", "type": "telephone_encounter.created_partial", "job_id": "12345", "data": { "telephone_encounter_id": "123e4567e89b12d3a456426614174000", "emr_encounter_id": "ECW-98765", "patient_mrn": "MRN-123456", "provider_id": "12241", "location_id": "25", "assigned_to_id": "12220", "reason": "Prescription refill", "refill_medication_name": "Lisinopril", "refill_failure_reason": "Failed to add medication refill: Timeout error" } } ``` Note: The `telephone_encounter.created_partial` event indicates that the encounter was successfully created in the EMR, but the requested medication refill could not be added. The encounter exists and is usable, but the refill will need to be added manually. #### Failure ##### General Failure ```json theme={null} { "id": "", "access_token_reference_id": "", "object": "event", "created": "2025-10-27T10:35:00Z", "type": "telephone_encounter.failed", "job_id": "12345", "data": { "telephone_encounter_id": "123e4567e89b12d3a456426614174000", "patient_mrn": "MRN-123456", "refill_medication_name": null, "failure_reason": "Failed to create telephone encounter in EMR" } } ``` ##### Medication Not Found Failure When a medication refill is requested but the medication cannot be found in the patient's available medications, the encounter is NOT created and a failure webhook is sent with the list of available medications: ```json theme={null} { "id": "", "access_token_reference_id": "", "object": "event", "created": "2025-10-27T10:35:00Z", "type": "telephone_encounter.failed", "job_id": "12345", "data": { "telephone_encounter_id": "123e4567e89b12d3a456426614174000", "patient_mrn": "MRN-123456", "refill_medication_name": "Aspirin", "failure_reason": "Medication \"Aspirin\" not found in available refillable medications. Available medications: [\"Lisinopril 10 MG\",\"Metformin 500 MG\",\"Atorvastatin 20 MG\"]" } } ``` # Add Action to Telephone Encounter Source: https://docs.usecobalt.com/api-reference/telephone-encounters/create-action POST /telephone-encounters/actions Adds a timestamped action log to an existing telephone encounter in the provider's EMR system. ### Request Parameters #### Required Fields * **appointment\_id** (string, required): The identifier of the telephone encounter to add the action to. This is **not** the "Ref #" shown in the EHR UI. Use one of the identifiers Cobalt provides — any of the following is accepted: * The `telephone_encounter_id` returned by [`POST /telephone-encounters`](/api-reference/telephone-encounters/create) when you created the encounter. * The `emr_encounter_id` from the `telephone_encounter.created` webhook payload. * The appointment `id` returned by the appointment fetch endpoints/webhooks (Cobalt resolves it to the underlying encounter). * **text** (string, required): The action text/note to add to the telephone encounter log ### How It Works When you add an action to a telephone encounter: 1. **Formatting**: The action is automatically formatted with the staff member's name, timestamp, and timezone 2. **Appending**: The formatted action is appended to the existing action log in the telephone encounter 3. **Timestamping**: Each action includes the exact date, time, and timezone when it was added ### Example Request ```bash theme={null} curl -X POST https://api.usecobalt.com/v1/telephone-encounters/actions \ -H 'Content-Type: application/json' \ -H 'client_id: ci_live_198908HJDKJSH98789OHKJL' \ -H 'client_secret: cs_live_9827hofdsklOYYHJLJh' \ -H 'access_token: 493JKLHIU98789hLKH9HHJH' \ -d '{ "appointment_id": "3fa85f6457174562b3fc2c963f66afa6", "text": "Patient called for prescription refill" }' ``` ### Example Response ```json theme={null} { "success": true, "message": "Action processing. A webhook event will be sent upon completion.", "action_id": "a7b8c9d0e1f2g3h4i5j6k7l8m9n0o1p2", "job_id": 12345 } ``` ### Response Fields * **success** (boolean): Indicates if the request was accepted for processing * **message** (string): Confirmation message * **action\_id** (string): Unique identifier for the action record (32-character hex string) * **job\_id** (integer): Reference to the job that will process this action ### Webhook Events You'll receive a webhook when the action is processed: #### Success Event ```json theme={null} { "id": "evt_abc123", "object": "event", "type": "telephone_encounter_action.created", "created": "2026-01-21T20:15:30.000Z", "job_id": "12345", "access_token_reference_id": "user_123", "data": { "action_id": "a7b8c9d0e1f2g3h4i5j6k7l8m9n0o1p2", "emr_appointment_id": "3784895", "text": "Patient called for prescription refill" } } ``` # Get Visit Statuses Source: https://docs.usecobalt.com/api-reference/visit-statuses/get GET /visit-statuses Returns a list of available visit statuses. ### Example Request ```bash theme={null} curl -X GET https://api.usecobalt.com/v1/visit-types \ -H 'Content-Type: application/json' \ -H 'client_id: ci_live_198908HJDKJSH98789OHKJL' \ -H 'client_secret: cs_live_9827hofdsklOYYHJLJh' \ -H 'access_token: 493JKLHIU98789hLKH9HHJH' ``` ### Example Response ```json theme={null} { "success": true, "data": [ { "code": "CANC", "description": "Cancelled in advance" }, { "code": "R/S", "description": "Rescheduled" } ] } ``` This endpoint returns a list of all visit statuses configured for the account associated with the provided access token. Each status type includes a `code` and a `description`. The code can be used in our PATCH /appointments endpoint to update the status of an appointment. # Get Visit Types Source: https://docs.usecobalt.com/api-reference/visit-types/get GET /visit-types Returns a list of available visit types. ### Example Request ```bash theme={null} curl -X GET https://api.usecobalt.com/v1/visit-types \ -H 'Content-Type: application/json' \ -H 'client_id: ci_live_198908HJDKJSH98789OHKJL' \ -H 'client_secret: cs_live_9827hofdsklOYYHJLJh' \ -H 'access_token: 493JKLHIU98789hLKH9HHJH' ``` ### Example Response ```json theme={null} { "success": true, "data": [ { "code": "AWV", "description": "Medicare Annual Wellness Visit" }, { "code": "BH Pre-Adm", "description": "Behavioral Health Pre-Admission" }, { "code": "BH Visit", "description": "Behavioral Health Admission & Treatment" }, { "code": "Cer Med NB", "description": "Cervical Medial Branch NB" } ] } ``` This endpoint returns a list of all visit types configured for the account associated with the provided access token. Each visit type includes a `code` and a `description`. # Get Webhook Events Source: https://docs.usecobalt.com/api-reference/webhook-events/get GET /webhook-events Retrieve webhook events sent to your registered webhook URLs. Use this endpoint to retrieve webhook events that have been sent to your registered webhook URLs. ## Use Cases * Debug webhook delivery issues * Audit webhook event history * Recover missed webhook events after downtime * Verify event payloads during integration testing ## Common Event Types * `patient.insurance.added` - Insurance successfully added to patient * `patient.insurance.failed` - Insurance addition failed * `appointment.created` - Appointment created * `appointment.updated` - Appointment updated * `note.created` - Clinical note created ## Query Parameters * **job\_id** (string, optional): Filter by job ID. Returns only events associated with a specific asynchronous operation. Useful for [awaiting results](/docs/awaiting-results/overview) of a single API call. * **event\_type** (string, optional): Filter by event type (e.g., "patient.insurance.added", "appointment.created") * **delivery\_status** (string, optional): Filter by delivery status - "success" or "failed". Indicates whether the webhook event was delivered to the webhook URL on file * **start\_date** (string, optional): Filter events created on or after this date (ISO 8601 format: YYYY-MM-DD) * **end\_date** (string, optional): Filter events created on or before this date (ISO 8601 format: YYYY-MM-DD) * **page** (integer, optional): Page number (default: 1, min: 1) * **page\_size** (integer, optional): Number of events per page (default: 50, max: 100) * **sort** (string, optional): Sort order. Use "created\_at" for ascending (oldest first) or "-created\_at" for descending (newest first). Default: "-created\_at" ## Common Workflows ### Track Appointments You Created via the API To track all appointments you've successfully created through Cobalt's API, filter by `appointment.created` events: ```bash theme={null} curl -X GET "https://api.usecobalt.com/v1/webhook-events?event_type=appointment.created&start_date=2025-01-01" \ -H "client_id: your_client_id" \ -H "client_secret: your_client_secret" \ -H "access_token: your_access_token" ``` This returns all successful appointment creations since January 1, 2025. Each event includes the `appointment_id`, patient MRN, date/time, and provider information - allowing you to maintain a complete audit trail of appointments scheduled through your integration. **Response:** ```json theme={null} { "success": true, "webhook_events": [ { "event_id": "evt_xyz789ghi012", "event_type": "appointment.created", "webhook_url": "https://your-webhook-url.com/webhooks", "delivery_status": "success", "data": { "appointment_id": "appt123456789", "date_time": "2025-01-20T14:30:00", "timezone": "America/Los_Angeles", "provider_id": "prov_456", "secondary_provider_id": null, "provider_name": "Dr. Jane Smith", "mrn": "MRN123456" }, "created_at": "2025-01-15T11:00:00.000Z", "job_id": "67890", "access_token_reference_id": "your_reference_id" } ], "pagination": { "current_page": 1, "total_pages": 5, "total_count": 243, "page_size": 50 } } ``` ## Additional Example Requests ### Get Recent Events ```bash theme={null} curl -X GET "https://api.usecobalt.com/v1/webhook-events?page_size=20" \ -H "client_id: your_client_id" \ -H "client_secret: your_client_secret" \ -H "access_token: your_access_token" ``` ### Filter by Event Type ```bash theme={null} curl -X GET "https://api.usecobalt.com/v1/webhook-events?event_type=patient.insurance.added&page_size=10" \ -H "client_id: your_client_id" \ -H "client_secret: your_client_secret" \ -H "access_token: your_access_token" ``` ### Filter by Date Range ```bash theme={null} curl -X GET "https://api.usecobalt.com/v1/webhook-events?start_date=2025-01-01&end_date=2025-01-31" \ -H "client_id: your_client_id" \ -H "client_secret: your_client_secret" \ -H "access_token: your_access_token" ``` ### Get Page 2 ```bash theme={null} curl -X GET "https://api.usecobalt.com/v1/webhook-events?page=2&page_size=50" \ -H "client_id: your_client_id" \ -H "client_secret: your_client_secret" \ -H "access_token: your_access_token" ``` ## Example Response ### Patient Insurance Added Event ```json theme={null} { "success": true, "webhook_events": [ { "event_id": "evt_abc123def456", "event_type": "patient.insurance.added", "webhook_url": "https://your-webhook-url.com/webhooks", "delivery_status": "success", "data": { "patient_insurance_id": "xyz789abc123", "patient_mrn": "MRN123456" }, "created_at": "2025-01-15T10:30:00.000Z", "job_id": "12345", "access_token_reference_id": "your_reference_id" } ], "pagination": { "current_page": 1, "total_pages": 10, "total_count": 487, "page_size": 50 } } ``` ## Response Fields ### Top-Level Fields * **success** (boolean): Whether the request was successful * **webhook\_events** (array): Array of webhook event objects * **pagination** (object): Pagination metadata ### Event Object Fields * **event\_id** (string): Unique event identifier * **event\_type** (string): Event type (e.g., "patient.insurance.added", "appointment.created") * **webhook\_url** (string): The URL the event was sent to * **delivery\_status** (string): Delivery status - "success" or "failed". Indicates whether the webhook event was delivered to the webhook URL on file * **data** (object): Event-specific payload data * **created\_at** (string): ISO 8601 timestamp of event creation * **job\_id** (string, nullable): Job ID if this event is related to an asynchronous operation * **access\_token\_reference\_id** (string, nullable): The reference ID associated with your access token ### Pagination Object * **current\_page** (integer): Current page number * **total\_pages** (integer): Total number of pages * **total\_count** (integer): Total number of events matching the filter * **page\_size** (integer): Number of items per page The `data` field in each event contains event-specific information that varies depending on the event type. Refer to the webhook event docs for payload structures: [Operation Events](/docs/webhooks/operation-events), [Sync Events](/docs/webhooks/sync-events), [Account Events](/docs/webhooks/account-events). ## Pagination This endpoint supports page-based pagination: 1. Make an initial request with optional `page` and `page_size` parameters 2. Check the `pagination` object in the response for `total_pages` 3. Request subsequent pages by incrementing the `page` parameter 4. Continue until you reach `total_pages` # Get Webhook Event Source: https://docs.usecobalt.com/api-reference/webhook-events/get-by-id GET /webhook-events/{id} Retrieve a single webhook event by its event ID. Use this endpoint to retrieve a specific webhook event by its ID. ### Path Parameters * **id** (required): The unique identifier of the webhook event (the `event_id` from the list endpoint) ### Example Request ```bash theme={null} curl -X GET "https://api.usecobalt.com/v1/webhook-events/evt_abc123def456" \ -H "client_id: your_client_id" \ -H "client_secret: your_client_secret" \ -H "access_token: your_access_token" ``` ### Example Response ```json theme={null} { "success": true, "webhook_event": { "event_id": "evt_abc123def456", "event_type": "appointment.created", "webhook_url": "https://your-webhook-url.com/webhooks", "delivery_status": "success", "data": { "appointment_id": "appt123456789", "date_time": "2025-01-20T14:30:00", "timezone": "America/Los_Angeles", "provider_id": "prov_456", "secondary_provider_id": null, "provider_name": "Dr. Jane Smith", "mrn": "MRN123456" }, "created_at": "2025-01-15T11:00:00.000Z", "job_id": "67890", "access_token_reference_id": "your_reference_id" } } ``` ### Response Fields * **event\_id** (string): Unique event identifier * **event\_type** (string): Event type (e.g., "patient.insurance.added", "appointment.created") * **webhook\_url** (string): The URL the event was sent to * **delivery\_status** (string): Delivery status - "success" or "failed" * **data** (object): Event-specific payload data * **created\_at** (string): ISO 8601 timestamp of event creation * **job\_id** (string, nullable): Job ID if this event is related to an asynchronous operation * **access\_token\_reference\_id** (string, nullable): The reference ID associated with your access token ### Error Responses #### Webhook Event Not Found ```json theme={null} { "success": false, "message": "Webhook event not found." } ``` This error occurs when: * The event ID doesn't exist * The event belongs to a different user account The `data` field contains event-specific information that varies depending on the event type. Refer to the webhook event docs for payload structures: [Operation Events](/docs/webhooks/operation-events), [Sync Events](/docs/webhooks/sync-events), [Account Events](/docs/webhooks/account-events). # Create Webhook Source: https://docs.usecobalt.com/api-reference/webhooks/create POST /webhook Registers a webhook URL for your account to receive event notifications. Registers a webhook URL for your account. A webhook is registered per account, so this one registration receives events for every connection and user under your account. The response includes the account-wide signing secret that you should store securely - it is used to verify that incoming webhooks are actually from Cobalt. Only `client_id` and `client_secret` headers are required. The request body accepts a single field, `webhook_url`; there is no per-connection webhook scoping. ### Example Request ```bash theme={null} curl -X POST https://api.usecobalt.com/v1/webhook \ -H 'Content-Type: application/json' \ -H 'client_id: ci_live_198908HJDKJSH98789OHKJL' \ -H 'client_secret: cs_live_9827hofdsklOYYHJLJh' \ -d '{ "webhook_url": "https://api.example.com/webhook" }' ``` ### Example Response ```json theme={null} { "success": true, "message": "Webhook URL added successfully", "webhook_url": "https://api.example.com/webhook", "webhook_id": "wh_123456789", "webhook_secret_key": "a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2" } ``` Store the `webhook_secret_key` securely - you'll need it to verify webhook signatures. It is a single account-wide key shared across all of your webhook deliveries. # Delete Webhook Source: https://docs.usecobalt.com/api-reference/webhooks/delete DELETE /webhook/{id} Deletes a webhook endpoint. Once deleted, no further events will be sent to the endpoint. ### Example Request ```bash theme={null} curl -X DELETE https://api.usecobalt.com/v1/webhook/wh_123456789 \ -H 'Content-Type: application/json' \ -H 'client_id: ci_live_198908HJDKJSH98789OHKJL' \ -H 'client_secret: cs_live_9827hofdsklOYYHJLJh' ``` ### Example Response ```json theme={null} { "success": true, "message": "Webhook deleted successfully" } ``` # Get Webhooks Source: https://docs.usecobalt.com/api-reference/webhooks/get GET /webhook Returns the webhook URLs registered for your account and the account-wide signing secret. Returns every webhook URL registered under your account plus the single account-wide `webhook_secret_key`. Only `client_id` and `client_secret` headers are required. ### Example Request ```bash theme={null} curl -X GET https://api.usecobalt.com/v1/webhook \ -H 'Content-Type: application/json' \ -H 'client_id: ci_live_198908HJDKJSH98789OHKJL' \ -H 'client_secret: cs_live_9827hofdsklOYYHJLJh' ``` ### Example Response ```json theme={null} { "success": true, "webhook_urls": [ { "id": "wh_123456789", "url": "https://api.example.com/webhook" } ], "webhook_secret_key": "a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2" } ``` # Rotate Webhook Secret Source: https://docs.usecobalt.com/api-reference/webhooks/rotate-secret POST /webhook/rotate-secret Generates a new signing secret for your account. This rotates the single account-wide webhook signing key, so every webhook delivery under this account is signed with the new key going forward. Rotating the secret replaces the account-wide webhook signing key. Because the key is shared across every connection and webhook URL registered under your account, all future deliveries are signed with the new key. Update your verification logic before rotating, or immediately after, to avoid rejecting valid webhooks. ### Example Request ```bash theme={null} curl -X POST https://api.usecobalt.com/v1/webhook/rotate-secret \ -H 'Content-Type: application/json' \ -H 'client_id: ci_live_198908HJDKJSH98789OHKJL' \ -H 'client_secret: cs_live_9827hofdsklOYYHJLJh' ``` ### Example Response ```json theme={null} { "success": true, "message": "Webhook secret rotated successfully", "webhook_secret_key": "a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2" } ``` Only `client_id` and `client_secret` headers are required. Store the returned `webhook_secret_key` securely and use it to verify webhook signatures. # Send Test Webhook Source: https://docs.usecobalt.com/api-reference/webhooks/send-test-webhook POST /webhook/test Sends a test webhook event to verify your integration is working correctly. Sends a `test.event` webhook to all configured webhook URLs for your client. Use this endpoint to: * Verify your webhook endpoint is receiving events correctly * Test your webhook signature verification logic * Validate your event processing pipeline * Debug webhook delivery issues The test event will include the standard webhook structure with a `test.event` type. ### Prerequisites You must have at least one webhook URL configured before sending a test event. If you haven't configured a webhook URL yet, use the [Create Webhook](/api-reference/webhooks/create) endpoint first. ### Example Request ```bash theme={null} curl -X POST https://api.usecobalt.com/v1/webhook/test \ -H 'client_id: ci_live_198908HJDKJSH98789OHKJL' \ -H 'client_secret: cs_live_9827hofdsklOYYHJLJh' \ -H 'access_token: at_live_abc123def456' ``` ### Example Response ```json theme={null} { "success": true, "message": "Test webhook sent to 2 URL(s)", "webhook_urls_count": 2, "event": { "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" } } } ``` ### What Your Webhook Endpoint Will Receive Your webhook endpoint will receive the test event with a signature header that you should verify: ```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" } } ``` The test webhook will be signed with your webhook secret key. Make sure to verify the `cobalt-verification` header to ensure the webhook is authentic. See [Receiving Webhooks](/docs/webhooks/receiving) for signature verification details. ### Common Use Cases After configuring your first webhook URL, send a test event to confirm your endpoint is reachable and processing webhooks correctly. After updating your webhook handler code, send a test event to verify your changes work as expected before going live. If you're not receiving production webhooks, send a test event to isolate whether the issue is with your endpoint or with specific event types. Send a test event and verify the `cobalt-verification` header to ensure your signature verification logic is working correctly. ### Error Responses **404 - No Webhook URLs Configured** ```json theme={null} { "success": false, "message": "No webhook URLs configured. Please add a webhook URL first using POST /v1/webhook." } ``` **401 - Invalid Access Token** ```json theme={null} { "success": false, "message": "Invalid access token" } ``` **401 - Access Token Mismatch** ```json theme={null} { "success": false, "message": "Access token does not belong to this client" } ``` # Best Practices Source: https://docs.usecobalt.com/docs/awaiting-results/best-practices Recommended settings and patterns for polling Cobalt operations. ## Polling Interval A poll interval of **1 second** works well for most operations. Cobalt typically processes operations within a few seconds to a couple of minutes depending on the EHR system. If you want to reduce API calls, you can increase the interval to 2-3 seconds with minimal impact on perceived latency. ## Timeout Set a timeout based on the operation type: | Operation | Recommended Timeout | | - | - | | Appointments | 2 minutes | | Patients | 2 minutes | | Notes | 3 minutes | | Documents | 3 minutes | | Insurance | 2 minutes | Processing times vary by EHR system. If you experience frequent timeouts, increase the timeout rather than reducing the poll interval. ## Handle Timeouts Gracefully A timeout does not mean the operation failed. It means the result wasn't available within your wait window. The operation may still complete successfully in the background. If a timeout occurs: 1. **Log the `job_id`** so you can look it up later. 2. **Check back later** by calling `GET /v1/webhook-events?job_id={job_id}` at any point in the future. 3. **Don't retry the original operation** unless you've confirmed it failed. Creating a duplicate appointment or patient is worse than waiting a bit longer. ```jsx theme={null} try { const result = await awaitResult(jobId, { timeoutMs: 120000 }); // Handle success or failure } catch (error) { if (error.message.includes('Timed out')) { // The operation may still succeed - don't retry blindly console.log(`Operation still processing. Track with job_id: ${jobId}`); } } ``` ## When to Use Webhooks Instead Polling works well for low-volume, on-demand operations. For high-volume or background processing, [webhooks](/docs/webhooks/overview) are more efficient: * **Use polling** when a user is waiting for an immediate result (e.g., a "Schedule Appointment" button in your UI). * **Use webhooks** when processing records in bulk or when operations happen in the background without a user waiting. * **Use both** for the best of both worlds: webhooks for production event processing, polling for ad-hoc operations and debugging. You do not need to register a webhook URL to use the polling approach. Cobalt tracks all operation results internally regardless of whether you have webhooks configured. ## Supported Operations Any Cobalt API operation that returns a `job_id` in its response supports this polling pattern. This includes: * **Appointments** - Create and update * **Patients** - Create * **Notes** - Create * **Documents** - Upload * **Tasks** - Create * **Messages** - Create * **Insurance** - Add to patient * **Referring Providers** - Create * **Telephone Encounters** - Create # Implementation Source: https://docs.usecobalt.com/docs/awaiting-results/implementation Code examples for polling Cobalt API operations to completion. ## JavaScript (Node.js) This helper function polls `GET /v1/webhook-events` until a result for the given `job_id` appears or the timeout is reached. ```jsx theme={null} const axios = require('axios'); const COBALT_BASE_URL = 'https://api.usecobalt.com'; const headers = { 'Content-Type': 'application/json', 'client_id': process.env.COBALT_CLIENT_ID, 'client_secret': process.env.COBALT_CLIENT_SECRET, 'access_token': process.env.COBALT_ACCESS_TOKEN, }; /** * Poll for the result of an asynchronous Cobalt operation. * @param {string} jobId - The job_id returned from the initial API call. * @param {object} options * @param {number} options.timeoutMs - Max time to wait (default: 120000ms / 2 minutes). * @param {number} options.pollIntervalMs - Time between polls (default: 1000ms). * @returns {object} The webhook event object with the operation result. * @throws {Error} If the timeout is reached before a result is available. */ async function awaitResult(jobId, { timeoutMs = 120000, pollIntervalMs = 1000 } = {}) { const startTime = Date.now(); while (Date.now() - startTime < timeoutMs) { const response = await axios.get(`${COBALT_BASE_URL}/v1/webhook-events`, { headers, params: { job_id: jobId }, }); const events = response.data.webhook_events; if (events && events.length > 0) { return events[0]; } await new Promise(resolve => setTimeout(resolve, pollIntervalMs)); } throw new Error(`Timed out after ${timeoutMs}ms waiting for result (job_id: ${jobId})`); } ``` ### Creating an Appointment and Awaiting the Result ```jsx theme={null} async function createAppointmentSync(appointmentData) { // Step 1: Create the appointment const createResponse = await axios.post( `${COBALT_BASE_URL}/v1/appointments`, appointmentData, { headers } ); const { appointment_id, job_id } = createResponse.data.data; console.log(`Appointment queued: ${appointment_id}, polling for result...`); // Step 2: Wait for the outcome const event = await awaitResult(job_id); // Step 3: Handle the result if (event.event_type === 'appointment.created') { console.log('Appointment created successfully:', event.data); return { success: true, data: event.data }; } else { console.error('Appointment failed:', event.data.failure_reason); return { success: false, error: event.data.failure_reason }; } } // Usage const result = await createAppointmentSync({ mrn: '123456789', location: '123456789', datetime: '2025-03-15T14:00:00-05:00', provider: '123456789', type: 'follow_up', }); ``` ### Creating a Patient and Awaiting the Result ```jsx theme={null} async function createPatientSync(patientData) { const createResponse = await axios.post( `${COBALT_BASE_URL}/v1/patients`, patientData, { headers } ); const { patient_id, job_id } = createResponse.data.data; console.log(`Patient queued: ${patient_id}, polling for result...`); const event = await awaitResult(job_id); if (event.event_type === 'patient.created') { console.log('Patient created successfully:', event.data); return { success: true, data: event.data }; } else { console.error('Patient creation failed:', event.data.failure_reason); return { success: false, error: event.data.failure_reason }; } } ``` ## Python ```python theme={null} import requests import time COBALT_BASE_URL = "https://api.usecobalt.com" headers = { "Content-Type": "application/json", "client_id": COBALT_CLIENT_ID, "client_secret": COBALT_CLIENT_SECRET, "access_token": COBALT_ACCESS_TOKEN, } def await_result(job_id, timeout_seconds=120, poll_interval_seconds=1): """ Poll for the result of an asynchronous Cobalt operation. Args: job_id: The job_id returned from the initial API call. timeout_seconds: Max time to wait (default: 120 seconds). poll_interval_seconds: Time between polls (default: 1 second). Returns: The webhook event dict with the operation result. Raises: TimeoutError: If the timeout is reached before a result is available. """ start_time = time.time() while time.time() - start_time < timeout_seconds: response = requests.get( f"{COBALT_BASE_URL}/v1/webhook-events", headers=headers, params={"job_id": job_id}, ) response.raise_for_status() events = response.json().get("webhook_events", []) if events: return events[0] time.sleep(poll_interval_seconds) raise TimeoutError( f"Timed out after {timeout_seconds}s waiting for result (job_id: {job_id})" ) def create_appointment_sync(appointment_data): # Step 1: Create the appointment create_response = requests.post( f"{COBALT_BASE_URL}/v1/appointments", headers=headers, json=appointment_data, ) create_response.raise_for_status() data = create_response.json()["data"] appointment_id = data["appointment_id"] job_id = data["job_id"] print(f"Appointment queued: {appointment_id}, polling for result...") # Step 2: Wait for the outcome event = await_result(job_id) # Step 3: Handle the result if event["event_type"] == "appointment.created": print("Appointment created successfully:", event["data"]) return {"success": True, "data": event["data"]} else: print("Appointment failed:", event["data"]["failure_reason"]) return {"success": False, "error": event["data"]["failure_reason"]} ``` ## cURL You can also poll manually with cURL for testing or debugging: ```bash theme={null} # Step 1: Create the appointment curl -X POST https://api.usecobalt.com/v1/appointments \ -H 'Content-Type: application/json' \ -H 'client_id: your_client_id' \ -H 'client_secret: your_client_secret' \ -H 'access_token: your_access_token' \ -d '{ "mrn": "123456789", "location": "123456789", "datetime": "2025-03-15T14:00:00-05:00", "provider": "123456789", "type": "follow_up" }' # Response: { "success": true, "data": { "appointment_id": "abc123", "job_id": "67890" } } # Step 2: Poll for the result using the job_id curl -X GET "https://api.usecobalt.com/v1/webhook-events?job_id=67890" \ -H 'client_id: your_client_id' \ -H 'client_secret: your_client_secret' \ -H 'access_token: your_access_token' # Repeat until webhook_events array is non-empty ``` # Overview Source: https://docs.usecobalt.com/docs/awaiting-results/overview Wait for the result of asynchronous operations without setting up webhooks. Many Cobalt API operations are asynchronous. When you create an appointment, patient, or other record via the API, Cobalt returns immediately with a `job_id` and processes the request in the background. Normally, you'd set up [webhooks](/docs/webhooks/overview) to receive the result. **Awaiting results** is an alternative pattern that lets you poll for the outcome directly, giving you a synchronous-style workflow without needing a webhook endpoint. This is useful for: * Getting started quickly without configuring webhook infrastructure * Simple scripts or one-off integrations * Environments where receiving inbound HTTP requests is difficult * Prototyping and testing This pattern uses the [Get Webhook Events](/api-reference/webhook-events/get) endpoint to poll for results by `job_id`. Every asynchronous operation returns a `job_id` that you can use to track its outcome. ## How It Works 1. **Make the API call** - Create an appointment, patient, or other record. The response includes a `job_id`. 2. **Poll for the result** - Call `GET /v1/webhook-events?job_id={job_id}` on an interval until a matching event appears. 3. **Handle the outcome** - The event tells you whether the operation succeeded or failed, with full details. ``` POST /v1/appointments ──► { job_id: "12345" } │ ▼ GET /v1/webhook-events?job_id=12345 ──► (empty, not ready) │ wait 1 second ▼ GET /v1/webhook-events?job_id=12345 ──► (empty, not ready) │ wait 1 second ▼ GET /v1/webhook-events?job_id=12345 ──► { type: "appointment.created", data: {...} } ``` # Welcome Source: https://docs.usecobalt.com/docs/introduction Cobalt provides a unified API for healthcare data, making it easy to integrate with any EHR system. Our API handles the complexity of different EHR systems, providing you with a consistent interface for managing appointments, patient data, and clinical notes. ## Features * **Unified API**: One integration for all EHR systems * **Real-time Updates**: Webhook notifications for changes in the EHR * **Secure Authentication**: Authentication through Cobalt Link * **HIPAA Compliant**: Enterprise-grade security for healthcare data ## Getting Started 1. **Authentication**: Learn how to [authenticate](/docs/authentication) your requests 2. **Cobalt Link**: Implement [secure EHR authentication](/docs/link/overview) 3. **Webhooks**: Set up [real-time notifications](/docs/webhooks/overview) ## Agent-ready API context Copy these resources into your coding agent to give it the API contract and Cobalt-specific EHR guidance. ```text Copy for your agent theme={null} Use these Cobalt API resources: OpenAPI specification: https://docs.usecobalt.com/api-reference/openapi.json Agent guide: https://docs.usecobalt.com/llms.txt ``` The machine-readable API contract for every endpoint, with per-EMR support tags. An index of the concepts and endpoints, written for agents. Append `.md` to any docs page URL for its raw markdown. **Per-EMR support lives in the spec, not in these pages.** An operation's `x-supported-emrs` lists the EMRs that support that endpoint. A request field's `x-required-for-emrs` lists the EMRs that require it, and `x-allowed-values-for-emrs` maps each EMR to the values it accepts. Consult those tags rather than assuming support. ## Need Help? If you need assistance, please don't hesitate to: * Email us at [bryan@usecobalt.com](mailto:bryan@usecobalt.com) * Check our API reference documentation # Overview Source: https://docs.usecobalt.com/docs/link/overview ## **Overview** Cobalt Link is the client-side component that your users will interact with in order to link their EMR to Cobalt and allow you to access their EMR via the Cobalt API. Link is used to obtain a `public_token`, which can then be exchanged for an `access_token`, which is used to authenticate requests to the Cobalt API. **The Cobalt Link** flow has the following steps: 1. Call  `https://api.usecobalt.com/link/token/create` to create a `link_token` and pass it to your app's client. 2. Use the `link_token` to open Link for your user. In the `onSuccess` callback, Link will provide a temporary `public_token`. 3. Call `https://api.usecobalt.com/link/token/exchange` to exchange the `public_token` for a permanent `access_token`. 4. Store the `access_token` and use it to make API requests. # Setup Source: https://docs.usecobalt.com/docs/link/setup **The Cobalt Link** flow has the following steps: 1. Call  `https://api.usecobalt.com/link/token/create` to create a `link_token` and pass it to your app's client. 2. Use the `link_token` to open Link for your user. In the `onSuccess` callback, Link will provide a temporary `public_token`. 3. Call `https://api.usecobalt.com/link/token/exchange` to exchange the `public_token` for a permanent `access_token`. 4. Store the `access_token` and use it to make API requests. ## Detailed Steps ### Create Link Token The `/link/token/create` endpoint creates a `link_token`, which is required as a parameter when initializing Link. Once Link has been initialized, it returns a `public_token`, which can then be exchanged for an `access_token` via `/link/token/exchange` as part of the main Link flow. ```bash theme={null} curl -X POST https://api.usecobalt.com/link/token/create \ -H 'Content-Type: application/json' \ -H 'client_id: "%COBALT_CLIENT_ID%"' \ -H 'client_secret: "%COBALT_CLIENT_SECRET%"' \ -d '{ "user_id": "%REFERENCE_USER_ID%", "org_id": "%REFERENCE_ORG_ID%" }' ``` The request requires the following parameters: * `client_id` this is your Cobalt API client id. * `client_secret` this is your Cobalt API secret. * `user_id` this is a unique ID representing the end user. Typically, this will be a user ID number from your application. Personally identifiable information, such as an email address or phone number, should not be used in the `user_id`. * `org_id` this is a unique identifier for the organization that this user belongs to in your system. This is often a clinic or hospital. This association is important to handle any custom settings that an org has. You choose this identifier - it can be any string that uniquely identifies the organization in your system. #### Creating a New Organization If this is the first user from a new organization, you'll need to provide additional parameters to create the organization: * `emr_name` **(required for new orgs)** - The name of the EMR system. Contact support for a list of supported EMR systems. * `emr_instance_domain` **(required for new orgs)** - The EMR instance URL (e.g., `https://mypractice.example.com`). Domain requirements vary by EMR. **Example request for a new organization:** ```bash theme={null} curl -X POST https://api.usecobalt.com/link/token/create \ -H 'Content-Type: application/json' \ -H 'client_id: "%COBALT_CLIENT_ID%"' \ -H 'client_secret: "%COBALT_CLIENT_SECRET%"' \ -d '{ "user_id": "%REFERENCE_USER_ID%", "org_id": "%REFERENCE_ORG_ID%", "emr_name": "%EMR_NAME%", "emr_instance_domain": "https://your-emr-instance.com" }' ``` Example response: ```bash theme={null} { "success": true, "token": "189hisdf78w9r3ysdfhi" // used in next step. } ``` ### Initialize Client Link **Lightbox Mode** Lightbox Mode will activate the Cobalt Link experience in a modal rather than having the element appear directly embedded elsewhere on the page. The lightbox takes up the entire user's screen, darkening the background content with a semi-transparent overlay of the page and centering Cobalt’s Link UI in a modal located at the center of the user's screen. To utilize Lightbox mode, include the Javascript snippet on your site, before the closing `` tag. Make sure to attach a button's `onClick` event to call the `launch_cobalt()` function. Alternatively, you can call the `window.CobaltLink.init()` function directly within an event callback of your choice. `CobaltLink.init` accepts one argument, a configuration `Object`. The configuration object requires the following parameters: * `token` this is the temporary `link_token` from the [create link token step](https://www.notion.so/Getting-Started-9d469ab83e1e421abfc770feed375c65?pvs=21). * `onSuccess` this callback is called when a user successfully links their account. It takes one argument: the `public_token`. The `public_token` can then be used in the [exchange public token step](https://www.notion.so/Getting-Started-9d469ab83e1e421abfc770feed375c65?pvs=21). **New Window Mode** If you prefer to not include the Cobalt Link code in your site you can use the Cobalt dashboard to generate a Link to share with your users so they can enter their credentials. You will then be able to view the access\_token in the Cobalt dashboard. ### Exchange Public Token The `/link/token/exchange` endpoint exchanges a `public_token` for an `access_token`. ```bash theme={null} curl -X GET "https://api.usecobalt.com/link/token/exchange" \ -H 'Content-Type: application/json' \ -H 'client_id: "%COBALT_CLIENT_ID%"' \ -H 'client_secret: "%COBALT_CLIENT_SECRET%"' -G \ --data-urlencode "public_token=234p852489035y29083y4" \ ``` The request requires the following parameters: * `client_id` this is your Cobalt API client id. * `client_secret` this is your Cobalt API secret. * `public_token` this is the token acquired during the Initialize Client Link step. Example response: ```bash theme={null} { "success": true, "token": "9872394hjkldsfh987re9iwqhjosdffh9sdychosdofc" } ``` ## Examples ### Code snippet ```jsx theme={null} ``` ### Javascript ```jsx theme={null} Cobalt Demo
``` ### React ```jsx theme={null} import React, { useCallback, useState, useEffect } from "react"; // Custom hook from Cobalt for handling link operations. // Run `npm i @cobalt-technology/react-link` to install the package. import { useCobaltLink } from "@cobalt-technology/react-link"; export default function CobaltLinkDemo() { const [linkToken, setLinkToken] = useState(null); // State to store the link token received from the Cobalt API const [publicToken, setPublicToken] = useState(""); // State to store the public token received from the onSuccess callback const [isReadyToInit, setIsReadyToInit] = useState(false); // State to determine if the component is ready to initialize the Cobalt link // Callback function to handle success after receiving public token const onSuccess = useCallback((public_token) => { console.log("got the public_token: ", public_token); setPublicToken("Public token: " + public_token); // Exchange the public token for the access token const accessTokenResponse = await fetch(`https://api.usecobalt.com/link/token/exchange?public_token=${public_token}`, { method: 'GET', headers: { 'Content-Type': 'application/json', 'client_id': '%COBALT_CLIENT_ID%', 'client_secret': '%COBALT_CLIENT_SECRET%' }, }); const accessTokenData = await accessTokenResponse.json(); console.log('got the access_token: ', accessTokenData.token); // Now you can save the access_token and use it to call the API }, []); // Destructuring the necessary methods and states from the useCobaltLink hook const { init, loading } = useCobaltLink({ token: linkToken, onSuccess, }); // Effect to initialize the Cobalt link once conditions are met useEffect(() => { if (isReadyToInit && linkToken && !publicToken) { init(); } }, [isReadyToInit, linkToken, init]); // Function to request a link token from the Cobalt API const initLink = async () => { try { const response = await fetch( "https://api.usecobalt.com/link/token/create", { method: "POST", headers: { "Content-Type": "application/json", client_id: "%COBALT_CLIENT_ID%", client_secret: "%COBALT_CLIENT_SECRET%", }, body: JSON.stringify({ user_id: "%REFERENCE_USER_ID%", org_id: "%REFERENCE_ORG_ID%" }), } ); const data = await response.json(); if (data && data.token) { setLinkToken(data.token); setIsReadyToInit(true); } else { console.error("Failed to obtain link token:", data.message); } } catch (err) { console.error("Failed to setup integration:", err); } }; // Render a button that triggers the initLink function return (
); } ``` # Update Mode Source: https://docs.usecobalt.com/docs/link/update-mode Update mode allows users to update their EHR credentials or re-authenticate when their connection needs attention. ### **When to use update mode** Update mode refers to any Link session launched for an account that has already been linked. Update mode is used when an existing account linkage requires input from a user, such as to update credentials. One common use of update mode is to update authentication or authorization for a linked account. This can be required when access to an existing account stops working: if the end user changes a password, if multi-factor authentication (MFA) requirements change, or if the login becomes locked. ### **Resolving** `login_required` **errors** Receiving a `login_required` error or a `login_required` webhook indicates that the account should be re-initialized via update mode. Example 403 `login_required` response: ```bash theme={null} { "success": false, "error": "login_required", "message": "Account link expired. Please go through the integration setup again." } ``` If you receive the `login_required` error after calling a Cobalt endpoint, implement Link in update mode during the user flow, and ask the user to re-authenticate before proceeding to the next step in your flow. If you receive the `login_required` error in an API call response, or if you receive the `login_required` webhook, re-authenticate with Link in update mode the next time the user is in your app. You will need to tell your user (using in-app messaging and/or notifications such as email or text message) to return to your app to fix their Item. When resolving these errors, Cobalt will present an abbreviated re-authentication flow requesting only the minimum user input required. For example, if the account entered an error state because the user's OTP token expired, the user may be prompted to provide another OTP token, but not to fully re-login to their account. ### **Using update mode** To use update mode for an Item, initialize Link with a `link_token` configured with the `access_token` for the account that you wish to update. ```bash theme={null} curl -X POST https://api.usecobalt.com/link/token/create \ -H 'Content-Type: application/json' \ -H 'client_id: "%COBALT_CLIENT_ID%"' \ -H 'client_secret: "%COBALT_CLIENT_SECRET%"' \ -d '{ "user_id": "%REFERENCE_USER_ID%", "org_id": "%REFERENCE_ORG_ID%", "access_token": "%EXISTING_ACCESS_TOKEN%" }' ``` Link auto-detects the appropriate EHR and handles the credential and multi-factor authentication process, if needed. An account’s `access_token` does not change when using Link in update mode, so there is no need to repeat the exchange token process. # Credentials & Access Source: https://docs.usecobalt.com/docs/managed-accounts/credentials Managed account status, credential visibility, and storage ## Managed account status Managed connections show a **Managed** badge on the Linked Accounts page in the dashboard, so you can always see which accounts Cobalt maintains and which your team owns. ## Accessing credentials You retain full access to every managed account. Privileged admins can view a managed account's current username and password on demand in the dashboard, and generate a current MFA code to complete a direct EHR login. Every credential view is audit logged. ## How credentials are stored Credentials are stored encrypted with per-customer keys and are never sent over email. For details on storage, see our security documentation or ask us for the current security posture summary. # Overview Source: https://docs.usecobalt.com/docs/managed-accounts/overview Cobalt owns the EHR account lifecycle: activation, MFA, and password rotation ## Overview With Managed Accounts, Cobalt owns the entire lifecycle of the EHR user account behind a connection. Your team never logs into the EHR. Cobalt activates the account, sets up and manages multi-factor authentication, and rotates the password ahead of every expiry policy, so the connection stays live without anyone on your side maintaining credentials. Contact us to enable Managed Accounts for your account. ## Responsibilities | Who | Responsible for | | - | - | | Clinic | Provisioning the EHR user account and setting its MFA policy | | You | Getting the fresh account connected to Cobalt. Nothing after that. | | Cobalt | Account activation, MFA setup and management, password rotation ahead of every expiry, connection uptime | See [Setup](/docs/managed-accounts/setup) for the connection steps and [Credentials & Access](/docs/managed-accounts/credentials) for how you stay in control of the account. # Setup Source: https://docs.usecobalt.com/docs/managed-accounts/setup Provision a fresh EHR account and connect it to Cobalt ## 1. The clinic provisions the account A practice administrator creates a new EHR user account for the integration: a username and a starting password, with multi-factor authentication required per the practice's policy. Any clinic can do this; nothing about their process changes. ## 2. Do not log in A brand-new EHR account requires first-login steps: selecting a security image, resetting the password, and enrolling multi-factor authentication. **None of these should happen.** The account must reach Cobalt in its fresh, never-logged-in state, because Cobalt performs the activation itself. If an account has already been logged into before connecting, contact us before proceeding. ## 3. Connect it to Cobalt Use any of the standard connection methods, and have whoever holds the credentials enter them: * **Send a Link URL** from the [Cobalt dashboard](https://app.usecobalt.com) directly to clinic staff, so the credentials never pass through your team * **Embed Cobalt Link** in your own product with the [React client](/docs/link/setup) * **Use the API** to [create a Link token](/api-reference/link/create-token) and drive the flow yourself Mark the connection as managed when you create it: check **Managed account** in the dashboard's send-link form, or set the managed flag when creating the Link token via API. ## 4. Cobalt takes it from there Cobalt activates the account (the first-login steps above), enrolls multi-factor authentication, and rotates the password, so the connection runs on a credential that only Cobalt holds. From then on, passwords rotate ahead of every expiry policy and MFA stays managed. Syncing begins as soon as the connection is live. # Account Events Source: https://docs.usecobalt.com/docs/webhooks/account-events Account events alert you when an end-user's stored EHR credentials need attention. They're not tied to a specific operation — Cobalt's authentication layer fires them when it detects a credential issue during any login attempt. There are two account events: * **`access_token.login_required`** — credentials are no longer valid; the user must re-authenticate via Cobalt Link before further operations can succeed. * **`access_token.password_change_warning`** — the EHR is prompting the user to change their password. Re-authenticate proactively to avoid an interruption. For both, surface `data.message` to the affected user and direct them to [Update Mode](/docs/link/update-mode) to refresh their credentials. ## Identifying the credential A connection can hold more than one set of EHR credentials, and an account event is always about exactly one of them. Both events carry `credential_id`, the ID of that credential set, so you do not have to guess which login needs attention. `credential_id` is also the ID the credential endpoints take, so you can act on the credential straight from the event: * [Disable a credential](/api-reference/account/disable-credential) — stop Cobalt using it for EHR sessions, leaving it in place. * [Delete a credential](/api-reference/account/delete-credential) — remove it entirely. * [Enable a credential](/api-reference/account/enable-credential) — put a disabled credential back into rotation. The last active credential set on a connection cannot be disabled or deleted. `credential_id` is present on the envelope and repeated inside `data`. Treat it as nullable: in the rare case where Cobalt cannot resolve the credential set behind an event, it is `null`. Fall back to `access_token_id` to identify the connection. ## Payload Envelope ```json theme={null} { "id": "550e8400-e29b-41d4-a716-446655440030", "access_token_reference_id": "your-reference-id", "access_token_id": "550e8400-e29b-41d4-a716-446655440000", "credential_id": "550e8400-e29b-41d4-a716-446655440010", "object": "event", "created": "2025-10-21T14:30:00.000Z", "type": "access_token.login_required", "data": { // Event-specific payload } } ``` | Field | Type | Description | | - | - | - | | `id` | string | Unique identifier for this event | | `access_token_reference_id` | string | Your reference ID for the affected end-user | | `access_token_id` | string | UUID of the specific connection (link access token) that generated this event | | `credential_id` | string | UUID of the credential set the event is about. Pass it to the credential endpoints to disable, delete, or re-enable that credential. `null` if Cobalt could not resolve it | | `object` | string | Always `"event"` | | `created` | string | ISO 8601 timestamp when the event was created | | `type` | string | The event type | | `data` | object | Event-specific data payload | ## Event Types Triggered when an end-user's stored EHR credentials are no longer valid (e.g. password changed in the EHR, account locked, or password expired). The user must re-authenticate via Cobalt Link before further operations can succeed. **Payload Example:** ```json theme={null} { "id": "550e8400-e29b-41d4-a716-446655440030", "access_token_reference_id": "clinic_1", "access_token_id": "550e8400-e29b-41d4-a716-446655440000", "credential_id": "550e8400-e29b-41d4-a716-446655440010", "object": "event", "created": "2025-10-21T14:30:00.000Z", "type": "access_token.login_required", "data": { "access_token_reference_id": "clinic_1", "credential_id": "550e8400-e29b-41d4-a716-446655440010", "emr_instance_domain": "myclinic.eclinicalworks.com", "message": "Invalid credentials detected - login required.", "error_type": "password_expired" } } ``` **Data Fields:** | Field | Type | Description | | - | - | - | | `access_token_reference_id` | string | Your reference ID for the affected end-user | | `credential_id` | string | UUID of the credential set that can no longer sign in. `null` if Cobalt could not resolve it | | `emr_instance_domain` | string | The EHR instance domain associated with the access token | | `message` | string | Human-readable explanation, including any next-step instructions | | `error_type` | string | Optional. Specific reason category (e.g. `password_expired`) | Triggered when an EHR prompts the user to change their password but the prompt was deferred. This is an early warning that a password change will be required soon. **Payload Example:** ```json theme={null} { "id": "550e8400-e29b-41d4-a716-446655440031", "access_token_reference_id": "clinic_1", "access_token_id": "550e8400-e29b-41d4-a716-446655440000", "credential_id": "550e8400-e29b-41d4-a716-446655440010", "object": "event", "created": "2025-10-21T14:30:00.000Z", "type": "access_token.password_change_warning", "data": { "access_token_reference_id": "clinic_1", "credential_id": "550e8400-e29b-41d4-a716-446655440010", "emr_instance_domain": "myclinic.eclinicalworks.com", "message": "Password change required in eCW. After changing your password in eCW, please update your Cobalt account by following the instructions at https://docs.usecobalt.com/docs/link/update-mode" } } ``` **Data Fields:** | Field | Type | Description | | - | - | - | | `access_token_reference_id` | string | Your reference ID for the affected end-user | | `credential_id` | string | UUID of the credential set whose password is expiring. `null` if Cobalt could not resolve it | | `emr_instance_domain` | string | The EHR instance domain associated with the access token | | `message` | string | Human-readable explanation with next-step instructions | # Operation Events Source: https://docs.usecobalt.com/docs/webhooks/operation-events Operation events fire when Cobalt performs an automated action inside the 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. 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. ## Payload Envelope Every operation event uses this envelope: ```json theme={null} { "id": "550e8400-e29b-41d4-a716-446655440000", "access_token_reference_id": "your-reference-id", "access_token_id": "7b4e9f21-c83a-4d56-b107-332f8a1c0e94", "object": "event", "created": "2025-10-21T14:30:00.000Z", "type": "appointment.created", "job_id": "12345", "action": "cobalt_operation", "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 | | `access_token_id` | string | UUID of the specific connection (link access token) that generated this event | | `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 | | `action` | string | Always `"cobalt_operation"` on operation events. The matching [Sync Events](/docs/webhooks/sync-events) carry `"ehr_sync"`, so shared event types like `patient.updated` can be told apart by this field. | | `data` | object | Event-specific data payload | This envelope (with `object: "event"` and `created`) applies to operation, sync, and on-demand **live-fetch** events (`appointment.live_fetch_completed`, `availability.live_fetch_completed`, `patient.live_fetch_completed`) alike. Live-fetch events carry `action: "sync"` and additionally include a `timestamp` field, a legacy alias of `created` retained for backward compatibility; prefer `created`. See [Receiving Webhooks](/docs/webhooks/receiving) for details. ## 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 Triggered when Cobalt successfully creates an appointment in the EHR. **Payload Example:** ```json theme={null} { "id": "759df7ac-2688-44a9-926c-58d36376b412", "access_token_reference_id": "clinic_1", "access_token_id": "550e8400-e29b-41d4-a716-446655440000", "object": "event", "created": "2025-07-28T16:05:48.929Z", "type": "appointment.created", "job_id": "12345", "data": { "appointment_id": "c5c0e613fc7d68a1c01ef4160483a0b6", "emr_appointment_id": "482129", "patient_mrn": "44235" } } ``` **Data Fields:** | Field | Type | Description | | - | - | - | | `appointment_id` | string | Cobalt's unique identifier for the appointment (dashless UUID) | | `emr_appointment_id` | string | The EHR's native appointment ID | | `patient_mrn` | string | Patient's Medical Record Number | Some EMRs include additional fields on this event. Triggered when Cobalt successfully updates an appointment in the EHR. **Payload Example:** ```json theme={null} { "id": "508c368e7de13b40f9397eec966d0329", "access_token_reference_id": "clinic_1", "access_token_id": "550e8400-e29b-41d4-a716-446655440000", "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 | 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", "access_token_id": "550e8400-e29b-41d4-a716-446655440000", "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 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", "access_token_id": "550e8400-e29b-41d4-a716-446655440000", "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 ## Patient Events 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", "access_token_id": "550e8400-e29b-41d4-a716-446655440000", "object": "event", "created": "2025-10-21T14:30:00.000Z", "type": "patient.created", "job_id": "12345", "action": "cobalt_operation", "data": { "patient_id": "xyz789uvw012", "mrn": "MRN123456", "eligibility_information": [ { "insurance_name": "Aetna", "status": "Active" } ] } } ``` **Data Fields:** | Field | Type | Description | | - | - | - | | `patient_id` | string | Cobalt's unique identifier for the patient | | `mrn` | string | Patient's EHR-native identifier (Medical Record Number) | | `eligibility_information` | array | Present only when the create was submitted with `check_eligibility=true`. Availability depends on EHR support. | To retrieve the full patient record (including `ehr_id` and demographics), call `GET /v1/patients` filtered by the returned `mrn`. 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", "access_token_id": "550e8400-e29b-41d4-a716-446655440000", "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 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", "access_token_id": "550e8400-e29b-41d4-a716-446655440000", "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 | 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", "access_token_id": "550e8400-e29b-41d4-a716-446655440000", "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 | Triggered when Cobalt successfully adds insurance information for a patient. **Payload Example:** ```json theme={null} { "id": "evt_abc123def456", "access_token_reference_id": "clinic_1", "access_token_id": "550e8400-e29b-41d4-a716-446655440000", "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", "plan_end_date": null, "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 / subscriber number (as provided) | | `group_id` | string | Insurance group ID | | `plan_begin_date` | string | Insurance plan start date (YYYY-MM-DD) | | `plan_end_date` | string \| null | Coverage end date (YYYY-MM-DD), null for open-ended coverage | | `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` is present in the success payload for some EMRs and absent for others, so the payload above may carry more or fewer fields than your integration returns. 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", "access_token_id": "550e8400-e29b-41d4-a716-446655440000", "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", "plan_end_date": "2026-12-31", "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 / subscriber number 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) | | `plan_end_date` | string \| null | Effective coverage end date (YYYY-MM-DD), null for open-ended coverage | | `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 | 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", "access_token_id": "550e8400-e29b-41d4-a716-446655440000", "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 | ## Task Events 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", "access_token_id": "550e8400-e29b-41d4-a716-446655440000", "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) | 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", "access_token_id": "550e8400-e29b-41d4-a716-446655440000", "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 ## Codes Events Triggered when Cobalt finishes adding ICD-10 diagnosis codes to an encounter. `icd` reports which codes were applied versus skipped as duplicates, not matched in the EMR catalog, or rejected as invalid for the encounter date. **Payload Example:** ```json theme={null} { "id": "550e8400-e29b-41d4-a716-446655440021", "access_token_reference_id": "clinic_1", "access_token_id": "550e8400-e29b-41d4-a716-446655440000", "object": "event", "created": "2025-10-21T14:30:00.000Z", "type": "codes.added", "job_id": "12345", "data": { "appointment_id": "APPT-12345", "operation": "add", "icd": { "added": ["E11.9", "I10"], "skipped": [], "unmatched": [], "invalid": [] } } } ``` **Data Fields:** | Field | Type | Description | | - | - | - | | `appointment_id` | string | The appointment whose encounter was updated | | `operation` | string | Always `add` | | `icd.added` | string\[] | Codes newly added to the encounter | | `icd.skipped` | string\[] | Codes already present (deduplicated) | | `icd.unmatched` | string\[] | Codes not found in the EMR catalog | | `icd.invalid` | string\[] | Codes the EMR rejected as invalid for the encounter date | Triggered when Cobalt finishes removing ICD-10 diagnosis codes from an encounter. `not_found` lists codes that were not on the encounter. **Payload Example:** ```json theme={null} { "id": "550e8400-e29b-41d4-a716-446655440022", "access_token_reference_id": "clinic_1", "access_token_id": "550e8400-e29b-41d4-a716-446655440000", "object": "event", "created": "2025-10-21T14:30:00.000Z", "type": "codes.removed", "job_id": "12346", "data": { "appointment_id": "APPT-12345", "operation": "remove", "icd": { "removed": ["I10"], "not_found": [] } } } ``` **Data Fields:** | Field | Type | Description | | - | - | - | | `appointment_id` | string | The appointment whose encounter was updated | | `operation` | string | Always `remove` | | `icd.removed` | string\[] | Codes removed from the encounter | | `icd.not_found` | string\[] | Requested codes that were not on the encounter | Triggered when a codes add or remove operation terminally fails. **Payload Example:** ```json theme={null} { "id": "550e8400-e29b-41d4-a716-446655440023", "access_token_reference_id": "clinic_1", "access_token_id": "550e8400-e29b-41d4-a716-446655440000", "object": "event", "created": "2025-10-21T14:30:00.000Z", "type": "codes.failed", "job_id": "12345", "data": { "appointment_id": "APPT-12345", "operation": "add", "status": "failed", "failure_reason": "eClinicalWorks rejected the diagnosis-code change.", "reasons": [ { "code": "ECW_REJECTED_CHANGE", "description": "eClinicalWorks rejected the diagnosis-code change." } ] } } ``` **Data Fields:** | Field | Type | Description | | - | - | - | | `appointment_id` | string | The appointment the operation targeted | | `operation` | string | `add` or `remove` | | `failure_reason` | string | Legacy human-readable error message | | `reasons` | array | Structured error objects with `code` and `description` | **Common Error Codes:** * `ECW_REJECTED_CHANGE` — The EMR rejected the diagnosis-code change * `UPSTREAM_TEMPORARILY_UNAVAILABLE` — The EMR was temporarily unavailable; the operation is retried automatically and this only fires if retries are exhausted * `UNKNOWN_ERROR` — Unspecified terminal failure ## Note Events 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", "access_token_id": "550e8400-e29b-41d4-a716-446655440000", "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. 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", "access_token_id": "550e8400-e29b-41d4-a716-446655440000", "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:** * `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 ## Document Events 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", "access_token_id": "550e8400-e29b-41d4-a716-446655440000", "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 | 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", "access_token_id": "550e8400-e29b-41d4-a716-446655440000", "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 Triggered when Cobalt fails to upload a document to the EHR. **Payload Example:** ```json theme={null} { "id": "550e8400-e29b-41d4-a716-446655440006", "access_token_reference_id": "clinic_1", "access_token_id": "550e8400-e29b-41d4-a716-446655440000", "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 ## Message Events 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", "access_token_id": "550e8400-e29b-41d4-a716-446655440000", "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 | 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", "access_token_id": "550e8400-e29b-41d4-a716-446655440000", "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 ## Telephone Encounter Events 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", "access_token_id": "550e8400-e29b-41d4-a716-446655440000", "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) | 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", "access_token_id": "550e8400-e29b-41d4-a716-446655440000", "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 | The encounter itself was created successfully — only the medication refill portion failed. Follow up manually to add the refill or notify the requester. 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", "access_token_id": "550e8400-e29b-41d4-a716-446655440000", "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 | 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", "access_token_id": "550e8400-e29b-41d4-a716-446655440000", "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 | 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", "access_token_id": "550e8400-e29b-41d4-a716-446655440000", "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 | ## Order Events Triggered when a catalog search started via `POST /v1/orders/catalog/fetch` completes. The `items` array holds one page of orderable catalog entries; use their `item_id` values to create an order. **Payload Example:** ```json theme={null} { "id": "550e8400-e29b-41d4-a716-446655440021", "access_token_reference_id": "clinic_1", "access_token_id": "550e8400-e29b-41d4-a716-446655440000", "object": "event", "created": "2025-10-21T14:30:00.000Z", "type": "order_catalog.searched", "job_id": "12345", "data": { "type": "lab", "page": 1, "has_more": true, "items": [ { "item_id": "80048", "name": "Comprehensive Metabolic Panel", "billable": true }, { "item_id": "82947", "name": "Glucose, Serum", "billable": true } ] } } ``` **Data Fields:** | Field | Type | Description | | - | - | - | | `type` | string | Catalog searched: `lab`, `imaging`, or `procedure` | | `page` | integer | Page number of these results | | `has_more` | boolean | Whether more pages are available | | `items` | array | Catalog entries, each with `item_id`, `name`, and `billable` | Triggered when Cobalt successfully creates an order in your EHR system. **Payload Example:** ```json theme={null} { "id": "550e8400-e29b-41d4-a716-446655440022", "access_token_reference_id": "clinic_1", "access_token_id": "550e8400-e29b-41d4-a716-446655440000", "object": "event", "created": "2025-10-21T14:30:00.000Z", "type": "order.created", "job_id": "12345", "data": { "emr_order_id": "998877", "emr_encounter_id": "445566", "patient_mrn": "MRN123456" } } ``` **Data Fields:** | Field | Type | Description | | - | - | - | | `emr_order_id` | string | The EHR's native order ID | | `emr_encounter_id` | string | The EHR's encounter/appointment ID the order belongs to | | `patient_mrn` | string | Patient's Medical Record Number | Triggered when Cobalt fails to create an order in your EHR system. **Payload Example:** ```json theme={null} { "id": "550e8400-e29b-41d4-a716-446655440023", "access_token_reference_id": "clinic_1", "access_token_id": "550e8400-e29b-41d4-a716-446655440000", "object": "event", "created": "2025-10-21T14:30:00.000Z", "type": "order.create_failed", "job_id": "12345", "data": { "patient_mrn": "MRN123456", "failure_reason": "Order create failed after multiple attempts. Please try again later or contact support." } } ``` **Data Fields:** | Field | Type | Description | | - | - | - | | `patient_mrn` | string | Patient's Medical Record Number | | `failure_reason` | string | Human-readable explanation of the failure | Triggered when Cobalt successfully updates an order via `PATCH /v1/orders/:id`. **Payload Example:** ```json theme={null} { "id": "550e8400-e29b-41d4-a716-446655440024", "access_token_reference_id": "clinic_1", "access_token_id": "550e8400-e29b-41d4-a716-446655440000", "object": "event", "created": "2025-10-21T14:30:00.000Z", "type": "order.updated", "job_id": "12345", "data": { "emr_order_id": "998877", "emr_encounter_id": "445566", "patient_mrn": "MRN123456" } } ``` **Data Fields:** | Field | Type | Description | | - | - | - | | `emr_order_id` | string | The EHR's native order ID | | `emr_encounter_id` | string | The EHR's encounter/appointment ID the order belongs to | | `patient_mrn` | string | Patient's Medical Record Number | Triggered when Cobalt fails to update an order via `PATCH /v1/orders/:id`. **Payload Example:** ```json theme={null} { "id": "550e8400-e29b-41d4-a716-446655440025", "access_token_reference_id": "clinic_1", "access_token_id": "550e8400-e29b-41d4-a716-446655440000", "object": "event", "created": "2025-10-21T14:30:00.000Z", "type": "order.update_failed", "job_id": "12345", "data": { "patient_mrn": "MRN123456", "failure_reason": "Order update failed after multiple attempts. Please try again later or contact support." } } ``` **Data Fields:** | Field | Type | Description | | - | - | - | | `patient_mrn` | string | Patient's Medical Record Number | | `failure_reason` | string | Human-readable explanation of the failure | Triggered when Cobalt successfully creates a lab result for an order via `POST /v1/orders/:id/results`. **Payload Example:** ```json theme={null} { "id": "550e8400-e29b-41d4-a716-446655440026", "access_token_reference_id": "clinic_1", "access_token_id": "550e8400-e29b-41d4-a716-446655440000", "object": "event", "created": "2025-10-21T14:30:00.000Z", "type": "lab_result.created", "job_id": "12345", "data": { "lab_result_id": "lr_123456", "order_id": "ord_123456", "emr_order_id": "998877", "emr_encounter_id": "445566", "patient_mrn": "MRN123456" } } ``` **Data Fields:** | Field | Type | Description | | - | - | - | | `lab_result_id` | string | Cobalt's unique identifier for the lab result | | `order_id` | string | Cobalt's identifier for the order the result belongs to | | `emr_order_id` | string | The EHR's native order ID | | `emr_encounter_id` | string | The EHR's encounter/appointment ID the order belongs to | | `patient_mrn` | string | Patient's Medical Record Number | Triggered when Cobalt fails to create a lab result for an order via `POST /v1/orders/:id/results`. **Payload Example:** ```json theme={null} { "id": "550e8400-e29b-41d4-a716-446655440027", "access_token_reference_id": "clinic_1", "access_token_id": "550e8400-e29b-41d4-a716-446655440000", "object": "event", "created": "2025-10-21T14:30:00.000Z", "type": "lab_result.create_failed", "job_id": "12345", "data": { "lab_result_id": "lr_123456", "order_id": "ord_123456", "patient_mrn": "MRN123456", "failure_reason": "Lab result creation failed after multiple attempts. Please try again later or contact support." } } ``` **Data Fields:** | Field | Type | Description | | - | - | - | | `lab_result_id` | string | Cobalt's identifier for the lab result | | `order_id` | string | Cobalt's identifier for the order the result belongs to | | `patient_mrn` | string | Patient's Medical Record Number | | `failure_reason` | string | Human-readable explanation of the failure | ## Test Event 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", "access_token_id": "550e8400-e29b-41d4-a716-446655440000", "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. You must have at least one webhook URL configured (`POST /v1/webhook`) before sending a test event. # Overview Source: https://docs.usecobalt.com/docs/webhooks/overview A webhook is an HTTP request used to provide push notifications. Cobalt sends webhooks to programmatically inform you about changes to EHR accounts associated with your integrations or the status of asynchronous processes. For example, Cobalt will send a webhook when an appointment is cancelled in an EHR or when Cobalt is finished processing an asynchronous chart note upload. ## Account-wide Scope A webhook is registered per account (link client), not per connection. A single registration receives events for every connection and user under that account. The HMAC signing key is likewise a single account-wide key: rotating it affects the signature of every future delivery across all of your connections. `POST /v1/webhook` accepts only a `webhook_url` field. There is no per-connection webhook scoping. To tell which connection an event came from, read the `access_token_id` field on the event envelope. ## Best Practices * Always use HTTPS for your webhook URL. * Keep your webhook secret key secure and rotate it regularly. Remember that rotation applies account-wide. * Respond to the webhook request quickly to acknowledge receipt. # Receiving Source: https://docs.usecobalt.com/docs/webhooks/receiving When an event occurs, Cobalt will send a POST request to your webhook URL with a JSON payload. The payload will include information about the event that occurred. ## Payload Envelope Every delivery is a JSON object describing one event. Cobalt sends three closely related envelope variants: * **Operation events** confirm actions Cobalt performed in the EHR (for example `appointment.created`, `patient.updated`, `test.event`). They carry `object: "event"`, a `created` timestamp, and `action: "cobalt_operation"`. * **EHR sync events** report changes Cobalt detected in the EHR (for example `appointment.updated`, `patient.created`, the `*.synced` events). Same shape as operation events, but `action: "ehr_sync"`. * **Live-fetch events** (`appointment.live_fetch_completed`, `availability.live_fetch_completed`, `patient.live_fetch_completed`) report the result of an on-demand fetch. They carry `object: "event"` and `created`, the same as every other event, and use `action: "sync"`. For backward compatibility they also include a `timestamp` field, a legacy alias of `created`; prefer `created`. They always fire when the fetch completes, regardless of your event subscriptions. **Operation / sync event:** ```json theme={null} { "id": "550e8400-e29b-41d4-a716-446655440000", "access_token_reference_id": "your-reference-id", "access_token_id": "7b4e9f21-c83a-4d56-b107-332f8a1c0e94", "object": "event", "created": "2025-10-21T14:30:00.000Z", "type": "appointment.created", "job_id": "12345", "action": "cobalt_operation", "data": { ... } } ``` **Live-fetch event:** ```json theme={null} { "id": "550e8400-e29b-41d4-a716-446655440000", "access_token_reference_id": "your-reference-id", "access_token_id": "7b4e9f21-c83a-4d56-b107-332f8a1c0e94", "object": "event", "created": "2025-10-21T14:30:00.000Z", "job_id": "12345", "type": "appointment.live_fetch_completed", "action": "sync", "timestamp": "2025-10-21T14:30:00.000Z", "data": { ... } } ``` Every delivery, including live-fetch events, carries `id`, `object` (always `"event"`), `created`, `type`, `data`, and `access_token_reference_id`. Live-fetch events additionally include a `timestamp` field, a legacy alias of `created` retained for backward compatibility; new integrations should read `created`. You can also read `webhook-timestamp` from the request headers, which is present on every delivery. Use `access_token_id` to attribute an event to a specific connection under your account. ## Headers Every webhook request includes the following headers: | Header | Description | | - | - | | `cobalt-verification` | HMAC-SHA256 signature of the payload for verifying authenticity | | `webhook-id` | Unique event identifier — use this to deduplicate deliveries | | `webhook-timestamp` | ISO 8601 timestamp of when the event was created | | `webhook-attempt` | Delivery attempt number (starting at `1`) | ## Verifying Signatures To ensure the webhook is coming from Cobalt, validate the `cobalt-verification` signature before processing the payload: ```jsx theme={null} const crypto = require('crypto'); function verifySignature(payload, signature, secretKey) { const expectedSignature = crypto.createHmac('sha256', secretKey) .update(JSON.stringify(payload)) .digest('hex'); return crypto.timingSafeEqual(Buffer.from(signature), Buffer.from(expectedSignature)); } // In your webhook handler app.post('/webhook', (req, res) => { const payload = req.body; const signature = req.headers['cobalt-verification']; if (!verifySignature(payload, signature, YOUR_WEBHOOK_SECRET_KEY)) { return res.status(401).send('Invalid signature'); } // Deduplicate using webhook-id const webhookId = req.headers['webhook-id']; if (alreadyProcessed(webhookId)) { return res.status(200).send('Already processed'); } // Process the webhook payload console.log('Received valid webhook:', payload); res.status(200).send('Webhook received'); }); ``` ## Retry Behavior If your endpoint returns a **5xx** status code or a network error occurs, Cobalt will retry delivery **once** (2 total attempts) with a 1-second delay. The retry uses a shorter 2-second timeout since your server should already be warm. Each retry increments the `webhook-attempt` header. Cobalt does **not** retry on: * **4xx** status codes (except 429) — these indicate a client-side configuration issue * **Timeouts** — if your server received the request but was slow to respond, retrying could cause duplicate processing Return a 2xx response as quickly as possible. Process the event asynchronously to avoid timeouts. If your endpoint takes longer than 5 seconds to respond, the request will time out. # Setup Source: https://docs.usecobalt.com/docs/webhooks/setup ## Setting up Webhooks To use webhooks, you need to set up a webhook endpoint URL in your application that can receive POST requests from Cobalt. This URL should be publicly accessible and use HTTPS for security. You can set up multiple webhook urls (eg. one for development and one for production) and Cobalt will send the same webhook event to each url. ### Register To register a new webhook, POST `/v1/webhook`. Example request: ```bash theme={null} curl -X POST https://api.usecobalt.com/v1/webhook \ -H 'Content-Type: application/json' \ -H 'client_id: "%COBALT_CLIENT_ID%"' \ -H 'client_secret: "%COBALT_CLIENT_SECRET%"' \ -d '{ "webhook_url": "https://your-domain.com/webhook" // https is required }' ``` Example response: ```bash theme={null} { "success": true, "message": "Webhook registered successfully", "webhook_secret_key": "your_webhook_secret_key" } ``` ### Get Configuration To retrieve your current webhook configuration, GET `/v1/webhook`. Example request: ```bash theme={null} curl -X GET https://api.usecobalt.com/v1/webhook \ -H 'Content-Type: application/json' \ -H 'client_id: ci_live_198908HJDKJSH98789OHKJL' \ -H 'client_secret: cs_live_9827hofdsklOYYHJLJh' ``` Example response: ```bash theme={null} { "success": true, "webhook_url": "https://your-domain.com/webhook", "webhook_urls": [ { "id": "987654321", "url": "https://dev.your-domain.com/cobalt-webhook" }, { "id": "123456789", "url": "https://your-domain.com/cobalt-webhook" } ], "webhook_secret_key": "your_webhook_secret_key" } ``` ### Remove a Webhook URL To remove a webhook url, DELETE `/v1/webhook/:id`. Example request: ```bash theme={null} curl -X DELETE https://api.usecobalt.com/v1/webhook/987654321 \ -H 'Content-Type: application/json' \ -H 'client_id: ci_live_198908HJDKJSH98789OHKJL' \ -H 'client_secret: cs_live_9827hofdsklOYYHJLJh' ``` Example response: ```bash theme={null} { "success": true, "message": "Webhook URL removed successfully" } ``` ### Rotate Secret To rotate your webhook secret key, POST `/v1/webhook/rotate-secret`. ```bash theme={null} curl -X POST https://api.usecobalt.com/v1/webhook/rotate-secret \ -H 'Content-Type: application/json' \ -H 'client_id: "%COBALT_CLIENT_ID%"' \ -H 'client_secret: "%COBALT_CLIENT_SECRET%"' ``` Example response: ```bash theme={null} { "success": true, "message": "Webhook secret rotated successfully", "webhook_secret_key": "your_new_webhook_secret_key" } ``` ## Overriding the Destination Per Request By default, every event is delivered to all of your registered webhook URLs. To route the events for a single operation somewhere else instead, include a `callback_urls` array in the body of any asynchronous request (the `POST` and `PATCH` endpoints that return a `job_id`): ```json theme={null} { // ...your normal request fields... "callback_urls": ["https://your-domain.com/cobalt-webhook"] // https is required } ``` When `callback_urls` is provided, the completion event for that operation is delivered only to those URLs and not to your registered account URLs. Omit it and delivery is unchanged. Each URL must use HTTPS. # Sync Events Source: https://docs.usecobalt.com/docs/webhooks/sync-events Sync events fire when Cobalt detects changes that occurred directly in your EHR system. These keep you informed about activities your staff performed in the EHR itself — appointments scheduled, patient information updated, and statuses changed. `appointment.created` and `appointment.updated` also fire as [Operation Events](/docs/webhooks/operation-events) when triggered by an action Cobalt performed. The event types are the same; the trigger differs. ## Payload Envelope Every sync event uses this envelope: ```json theme={null} { "id": "508c368e7de13b40f9397eec966d0329", "access_token_reference_id": "your-reference-id", "access_token_id": "550e8400-e29b-41d4-a716-446655440000", "object": "event", "created": "2025-07-28T08:50:42.491-07:00", "type": "appointment.updated", "action": "ehr_sync", "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 | | `access_token_id` | string | UUID of the specific connection (link access token) that generated this event | | `object` | string | Always `"event"` | | `created` | string | ISO 8601 timestamp when the event was created | | `type` | string | The event type | | `action` | string | Always `"ehr_sync"` on sync events. The matching [Operation Events](/docs/webhooks/operation-events) carry `"cobalt_operation"`, so shared event types like `patient.updated` can be told apart by this field. | | `data` | object | Event-specific data payload | This envelope (with `object: "event"` and `created`) applies to sync, operation, and on-demand **live-fetch** events (`appointment.live_fetch_completed`, `availability.live_fetch_completed`, `patient.live_fetch_completed`) alike. Live-fetch events carry `action: "sync"` and additionally include a `timestamp` field, a legacy alias of `created` retained for backward compatibility; prefer `created`. See [Receiving Webhooks](/docs/webhooks/receiving) for details. ## Appointment Events Triggered when EHR staff create an appointment in your EHR (detected by Cobalt's sync). **Payload Example:** ```json theme={null} { "id": "759df7ac-2688-44a9-926c-58d36376b412", "access_token_reference_id": "clinic_1", "access_token_id": "550e8400-e29b-41d4-a716-446655440000", "object": "event", "created": "2025-07-28T16:05:48.929Z", "type": "appointment.created", "data": { "id": "0551c88b2249945151f967525ee3f7b4", "ehr_appointment_id": "482129", "patient_mrn": "44235", "patient_name": "John Doe", "datetime": "2025-07-28T16:00:00.000Z", "duration": 20, "location": "2", "status": "1", "provider_ehr_id": "1568", "secondary_provider_ehr_id": null, "provider_name": "Dr. Jose Gonzalez", "provider_npi": "1234567890", "appointment_type": "OFFICE VISIT", "appointment_mode": "office" } } ``` **Data Fields:** The full appointment record is included (same shape as `appointment.updated`, without `updated_fields`). Key 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 (EMR facility ID) | | `status` | string | Appointment status code | | `provider_ehr_id` | string \| null | Primary provider's EHR ID (for calendar/assignee mapping) | | `secondary_provider_ehr_id` | string \| null | Secondary provider's EHR ID | | `provider_name` | string | Primary provider's name | | `provider_npi` | string \| null | Primary provider's NPI | | `appointment_type` | string | Type of appointment | | `appointment_mode` | string | Mode (e.g., "office", "telehealth") | Triggered when EHR staff update an appointment in your EHR (detected by Cobalt's sync). **Payload Example:** ```json theme={null} { "id": "508c368e7de13b40f9397eec966d0329", "access_token_reference_id": "clinic_1", "access_token_id": "550e8400-e29b-41d4-a716-446655440000", "object": "event", "created": "2025-07-28T08:50:42.491-07:00", "type": "appointment.updated", "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_ehr_id": "1568", "secondary_provider_ehr_id": null, "provider_name": "Dr. Michael Denenberg", "provider_npi": "1234567890", "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_ehr_id` | string \| null | Primary provider's EHR ID (for calendar/assignee mapping) | | `secondary_provider_ehr_id` | string \| null | Secondary provider's EHR ID | | `provider_name` | string | Provider's name | | `provider_npi` | string \| null | Primary provider's NPI | | `appointment_type` | string | Type of appointment | | `appointment_mode` | string | Mode (e.g., "office", "telehealth") | | `updated_fields` | object | Fields that changed in this update | Triggered when an appointment's status changes in your EHR system. **Payload Example:** ```json theme={null} { "id": "508c368e7de13b40f9397eec966d0330", "access_token_reference_id": "clinic_1", "access_token_id": "550e8400-e29b-41d4-a716-446655440000", "object": "event", "created": "2025-07-28T09:15:22.491-07:00", "type": "appointment.status_updated", "data": { "id": "0551c88b2249945151f967525ee3f7b4", "ehr_appointment_id": "482129", "patient_mrn": "804", "patient_name": "John Doe", "datetime": "2025-07-28T16:00:00.000Z", "status": "checked-in", "previous_status": "scheduled", "provider_name": "Dr. Michael Denenberg" } } ``` **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) | | `status` | string | New appointment status | | `previous_status` | string | Previous appointment status | | `provider_name` | string | Provider's name | ## Patient Events Sync patient events carry the **full patient object** in `data` (not a subset), so you can update your own system without a follow-up `GET /v1/patients`. `patient.updated` additionally includes a `changes` map describing what changed. Note that sync events use `changes` (`{ old, new }` per field), whereas the Operation `patient.updated` event uses an `updated_fields` array of field names. Triggered when EHR staff create a new patient in your EHR (detected by Cobalt's sync). **Payload Example:** ```json theme={null} { "id": "508c368e7de13b40f9397eec966d0330", "access_token_reference_id": "clinic_1", "access_token_id": "550e8400-e29b-41d4-a716-446655440000", "object": "event", "created": "2025-07-28T10:18:02.491-07:00", "type": "patient.created", "action": "ehr_sync", "data": { "id": "65ed04f9d25fca3876f367d9ad94e3c3", "mrn": "MRN123456", "ehr_id": "123456", "first_name": "Jane", "last_name": "Smith", "middle_name": null, "address_street": "123 Main St", "address_city": "Springfield", "address_state": "IL", "address_zip": "62701", "phone": "555-123-4567", "cell_phone": "555-123-4568", "email": "jane.smith@example.com", "dob": "1985-06-15", "sex": "female", "status": "active", "insurance_name": "Aetna", "insurance_subscriber_number": "ABC123456", "pcp_first_name": "John", "pcp_last_name": "Doe" } } ``` **Data Fields:** The full patient object. Key identifiers: | Field | Type | Description | | - | - | - | | `id` | string | Cobalt's unique identifier for the patient (UUID without hyphens) | | `mrn` | string | Patient's Medical Record Number | | `ehr_id` | string | The EHR's native patient identifier | Triggered when EHR staff update patient information in your EHR (detected by Cobalt's sync). **Payload Example:** ```json theme={null} { "id": "508c368e7de13b40f9397eec966d0331", "access_token_reference_id": "clinic_1", "access_token_id": "550e8400-e29b-41d4-a716-446655440000", "object": "event", "created": "2025-07-28T10:20:15.491-07:00", "type": "patient.updated", "action": "ehr_sync", "data": { "id": "65ed04f9d25fca3876f367d9ad94e3c3", "mrn": "MRN123456", "ehr_id": "123456", "first_name": "Jane", "last_name": "Smith", "phone": "555-9999", "email": "jane.newemail@example.com", "status": "active", "changes": { "phone": { "old": "555-123-4567", "new": "555-9999" }, "email": { "old": "jane.smith@example.com", "new": "jane.newemail@example.com" } } } } ``` **Data Fields:** The full patient object (same shape as `patient.created`) plus: | Field | Type | Description | | - | - | - | | `changes` | object | Map of the fields that changed, each with its `old` and `new` value | **Fields that can appear in `changes`:** `first_name`, `last_name`, `middle_name`, `address_street`, `address_line2`, `address_city`, `address_state`, `address_zip`, `phone`, `cell_phone`, `home_phone`, `work_phone`, `email`, `dob`, `sex`, `status`, `insurance_name`, `insurance_subscriber_number`, `referred_to_provider_id`, `pcp_first_name`, `pcp_last_name`, `medications`, `problems`, `vitals`. Insurance changes made directly in the EHR do not currently emit a sync event. Insurance is synced into the patient record, but only Cobalt-initiated add/update calls fire `patient.insurance.added` / `patient.insurance.updated` (see [Operation Events](/docs/webhooks/operation-events)). ## Claim Events Triggered when a new claim is detected in your EHR (detected by Cobalt's sync). **Payload Example:** ```json theme={null} { "id": "759df7ac268844a9926c58d36376b412", "access_token_reference_id": "clinic_1", "access_token_id": "550e8400-e29b-41d4-a716-446655440000", "object": "event", "created": "2026-07-13T04:12:08.921Z", "type": "claim.created", "data": { "id": "0551c88b2249945151f967525ee3f7b4", "emr_claim_id": "73001", "emr_appointment_id": "264453", "emr_patient_id": "32191", "patient_name": "Jane Smith", "provider_name": "Dr. Michael Denenberg", "service_date": "2026-04-13", "claim_date": "2026-04-14", "claim_status": "PEN", "claim_status_description": "Pending", "claim_type": "0", "void_flag": false, "total_charge": "1233.87", "total_payment": "0.00", "net_payment": "0.00", "balance": "1233.87", "net_adjustment": "0.00", "patient_responsibility": "0.00", "icd_codes": [ { "code": "Z01.411", "primary": "1", "description": "Encounter for gynecological examination with abnormal findings", "icd_version": "10" } ] } } ``` **Data Fields:** | Field | Type | Description | | - | - | - | | `id` | string | Cobalt's unique identifier for the claim | | `emr_claim_id` | string | The EHR's native claim ID | | `emr_appointment_id` | string | The EHR's appointment/encounter ID the claim belongs to | | `emr_patient_id` | string | The EHR's patient ID | | `patient_name` | string | Patient's full name | | `emr_provider_id` | string | The EHR's provider ID | | `provider_name` | string \| null | Rendering provider's name | | `service_date` | string | Date of service (YYYY-MM-DD) | | `claim_date` | string | Claim date (YYYY-MM-DD) | | `claim_status` | string | EHR claim status code (e.g. `PEN`) | | `claim_status_description` | string | Human-readable claim status | | `claim_type` | string | EHR claim type code | | `void_flag` | boolean | Whether the claim is voided | | `total_charge` | string | Total billed amount | | `total_payment` | string | Total payment received | | `net_payment` | string | Net payment | | `balance` | string | Outstanding balance | | `net_adjustment` | string | Net adjustment | | `patient_responsibility` | string | Patient responsibility amount | | `icd_codes` | array | ICD-10 diagnosis codes on the claim | Triggered when a synced claim changes in your EHR. Only fires when a header field actually changed (for example a status, payment, or balance update), so unchanged claims re-seen during a sync do not emit an event. **Payload Example:** ```json theme={null} { "id": "759df7ac268844a9926c58d36376b413", "access_token_reference_id": "clinic_1", "access_token_id": "550e8400-e29b-41d4-a716-446655440000", "object": "event", "created": "2026-07-13T05:41:22.104Z", "type": "claim.updated", "data": { "id": "0551c88b2249945151f967525ee3f7b4", "emr_claim_id": "73001", "emr_appointment_id": "264453", "emr_patient_id": "32191", "patient_name": "Jane Smith", "service_date": "2026-04-13", "claim_status": "PAID", "claim_status_description": "Paid", "total_charge": "1233.87", "total_payment": "1050.00", "balance": "0.00", "updated_fields": { "claim_status": "PAID", "total_payment": "1050.00", "balance": "0.00" } } } ``` **Data Fields:** The full claim object (same fields as `claim.created`) plus: | Field | Type | Description | | - | - | - | | `updated_fields` | object | Map of the fields that changed, with their new values |