ES ↗
SOP-OD-001 · v1.0
Jun 11, 2026

Import Fee Schedule to Open Dental

Standard Operating Procedure · Internal Tools

Purpose

Bulk-import a fee schedule (UCR or other type) into Open Dental for an active client, using the create_fee_schedule.py script. This SOP ensures existing prices are not overwritten and that errors are caught before the write.

Estimated time: 15–20 min prep + ~6 min per 500 fees.
Requirements: Client with OD API enabled · CSV or PDF with the fees · Repo access + venv activated.

Visual flow

flowchart TD A[1. Verify client
has OD API] --> B[2. List existing
fee schedules] B --> C{Schedule
exists?} C -->|No| D[Create manually
in OD UI] C -->|Yes| E[3. Note the
FeeSchedNum] D --> E E --> F[4. Prepare CSV
from PDF or manual] F --> G[5. DRY-RUN
touches nothing] G --> H{Output
OK?} H -->|Typos /
weird prices| F H -->|Codes missing
in OD| I[Enable codes
in OD Definitions] I --> G H -->|All OK| J[6. Real import
--confirm] J --> K[7. Verify
in OD UI] K --> L[8. Optional:
Assign as UCR] style A fill:#4B6AF7,color:#fff,stroke:#415EDB style G fill:#F1CCB1,color:#1a1a2e,stroke:#c9690a style J fill:#2d8659,color:#fff,stroke:#2d8659 style L fill:#415EDB,color:#fff,stroke:#415EDB

Pre-checks

CheckHow to verify
Client existsls clients/<client-id>.json
OD API enabledIn the JSON: "has_od_api": true + customer_key present
venv activatedsource .venv/bin/activate (prompt starts with (.venv))
CSV readyRequired columns: ProcCode, Amount

Detailed steps

1Verify client and list existing fee schedules

Confirms the client is configured properly and shows which fee schedules already exist in Open Dental.

python create_fee_schedule.py <client-id> --list
FeeSchedNum   Type           Hidden  Description
-------------  --------------  -------  ----------------------------------------
53             Normal                   Office Fees
57             Normal                   Delta Dental PPO
66             Normal                   Office Fee 2026   ← the new one

2Create the fee schedule (if it doesn't exist)

BrandaCare policy: fee schedules are created manually in OD UI (not via API) to avoid accidental duplicates.

In Open Dental:

  1. Setup → Fee Schedules → Add
  2. Description: clear name (e.g. "Office Fee 2026")
  3. Type: Normal (UCR) or Insurance (PPO)
  4. Save → NOTE THE FeeSchedNum
If you prefer API-based creation: python create_fee_schedule.py <client-id> --create "Office Fee 2026" — confirm with the client first to avoid duplicate names.

3Prepare the CSV

Option A — from PDF (most common with new clients):

python parse_pdf_fee_schedule.py "Dr Smith Fees 2026.pdf" clients/smith_fees_2026.csv

The parser detects D#### Description $Amount lines and auto-flags likely typos (prices >$5000 or <$10).

Option B — manual CSV:

ProcCode,Amount,Description
D0120,150.00,Periodic oral evaluation
D0150,200.00,Comprehensive oral evaluation
...
Review parser warnings BEFORE import. Real example: D2915 detected as $13,500 when it should be $135. Fix in the CSV (or correct manually in OD post-import).

4Dry-run (CRITICAL — touches nothing)

python create_fee_schedule.py <client-id> \
    --csv clients/smith_fees_2026.csv \
    --to <FeeSchedNum> \
    --dry-run

Review the output carefully:

  • OK [dry] D0120 = $150.00 — will import
  • SKIP ⚠ code X not found in OD — ADA code not enabled in this client
  • TYPO Any price >$5000 that isn't major surgery/prosthetics

At the end you see:

✅ 516 fees simulated, 17 skipped
📄 List saved to: clients/smith_fees_2026_SKIPPED.csv

5Real import — --confirm

Point of no return. This writes to Open Dental production. Confirm that:
  • The fee schedule is empty in OD (if it has fees, they duplicate).
  • Parser typos have been corrected.
  • The FeeSchedNum matches the one from step 1.
python create_fee_schedule.py <client-id> \
    --csv clients/smith_fees_2026.csv \
    --to <FeeSchedNum> \
    --confirm

Progress every 50 fees. Takes ~6 min per 500 fees.

6Verify in OD

  • OD UI → Setup → Fee Schedules → your schedule → should show ≈ N fees created
  • Spot check: search 3-4 random codes and confirm the price
  • Review <csv>_SKIPPED.csv and forward to client to enable missing codes (optional)

7Optional — Assign as provider UCR

This makes new procedures use this fee schedule as the default price. Does not affect existing procedures.

python create_fee_schedule.py <client-id> \
    --assign-ucr <FeeSchedNum> \
    --to-provider <ProvNum> \
    --confirm
ProvNum is found in OD UI: Setup → Providers → click the provider → see "Provider Num" in the form.

Troubleshooting

ErrorCauseFix
client not foundTypo in client_idls clients/ and copy the exact name
code X not found in ODADA code not enabled in clientOD → Setup → Definitions → Procedure Codes → enable
this client has no OD APIMissing customer_key in JSONRequest from client (Google Secret Manager if internal)
Duplicate prices post-importDestination FeeSched wasn't emptyDelete manually in OD and re-import
Intermittent ReadTimeoutOD API slowRe-run — script retries automatically

Safety rules

Copy-paste commands

# 1. List
python create_fee_schedule.py <client-id> --list

# 2. Parse PDF (if needed)
python parse_pdf_fee_schedule.py "fee_schedule.pdf" output.csv

# 3. Dry-run
python create_fee_schedule.py <client-id> --csv path/to.csv --to <num> --dry-run

# 4. Real import
python create_fee_schedule.py <client-id> --csv path/to.csv --to <num> --confirm

# 5. Assign as UCR (optional)
python create_fee_schedule.py <client-id> --assign-ucr <num> --to-provider <prov> --confirm

Import history

DateClientFeeSchedResult
2026-06-11Casas Family Dentistry66 — Office Fee 2026516 fees created, 17 skipped