# Dawilo clinic bridge — v1

Keep your existing medical software. This server-side starter imports **new upcoming human-health appointments** from a mapped CSV export and receives signed Dawilo events into a durable inbox. It is not a certified connector for a named vendor, a medical-record migration, or a bidirectional agenda synchronizer.

Python 3.11+ with its standard library; no pip install. Designed for Windows, macOS and Linux; automated execution in this release is on Linux. Do not upload exports, state, API keys or webhook secrets into Dawilo's public directory. Download the kit to a private directory on the clinic's integration computer/server. On Windows, restrict that directory to the integration service account; Unix state files use mode 0600. Back up the state file with the vendor database.

## Connect in five steps

1. Ask the vendor for a documented API or a read-only CSV export, plus its version and data dictionary. Keep appointment IDs stable. Do not use patient name or phone as a record key. Do not write directly into undocumented vendor tables.
2. In Dawilo **Owner → API & partners**, create an inactive sandbox key with `appointments:write`, enable it and the API, and test fictional data. For live use create a separate live key and explicitly connect the public practice ID. `availability:read` is optional for slot previews; it is separate from write permission. Enable only the additional scopes needed by a pharmacy/lab.
3. Download `bridge.py`, copy `config.example.json` to `config.json`, and edit the origin, column mapping, date formats, encoding, practice ID and doctor IDs. `appointments.example.csv` is fictional; replace its date with a future date within 90 days. Missing birth dates must be corrected in the source, never guessed. Public guest booking has its own simpler form; the clinical partner import still requires birth date.
4. Put the key in the process environment as `DAWILO_API_KEY` using your service manager or secret store. Run:

   ```sh
   python3 bridge.py check --config config.json
   python3 bridge.py import --config config.json --csv appointments.csv
   ```

   On Windows use `py -3` instead of `python3`. `check` returns connected practice information and practitioner IDs from `GET /api/partner/v1/practice-configuration`. Preview validates the entire file locally and sends nothing. Check slot availability with the existing availability API; the server checks again during creation.
5. After reviewing the mapping and sandbox results, explicitly send the batch:

   ```sh
   python3 bridge.py import --config config.json --csv appointments.csv --apply
   ```

   Each successful line reports only its row number. A stopped batch is resumable with the same file, key and state. The clinic can continue using its existing software.

## Mapping and import boundaries

Required mapped columns: external appointment ID, external doctor ID, patient name, Algerian mobile number, birth date, date and time. Optional `service` defaults to consultation. `services` maps vendor terms to `consultation` or `controle`. Dates are parsed using explicit `date_format`/`birth_date_format`, e.g. `%d/%m/%Y`; appointment time is Algeria local HH:MM. No conversion from a guessed timezone. UTF-8 with BOM, semicolons, commas and explicit legacy encodings such as `cp1252` or `cp1256` are supported through configuration. Save Excel phone/ID columns as text so leading zeros survive. `.xlsx`, PDF, database backup and image files are not CSV.

The bridge creates appointments only. Re-running identical source rows skips completed imports or replays the original pending idempotency key. A changed row is stopped for review; it never overwrites a doctor's edits or creates another booking. For rescheduling/cancellation use the documented PATCH API with the last revision. On 409, fetch the current record, compare it with the intended change and ask the responsible clinic to resolve it. Never automatically retry with a fresh revision.

All rows are validated before a batch starts. A runtime conflict or network error stops the remaining batch; completed rows stay committed and the pending row stays in SQLite. There is no automatic timer: run the same command again after restoring connectivity. A clinic may schedule it only after this workflow passes acceptance testing. The server rate limit is 120 requests/minute/key; on 429 the bridge stops and displays `Retry-After`. Reuse the same file and state when retrying.

Do not delete state, change source IDs or switch keys to recover an uncertain import. API idempotency belongs to a key; rotating a key requires reconciling and transferring existing mappings before any re-import. The state is bound to server, environment, practice and key hash. It stores fingerprints, IDs and receipts, not CSV patient names, phone numbers or birth dates. The source CSV still contains private data and needs the clinic's normal retention/access controls. Multi-row imports are intentionally not one atomic batch.

## Signed webhook inbox

Register one public HTTPS port-443 endpoint per key through the existing webhooks API; retain its one-time `signing_secret` as `DAWILO_WEBHOOK_SECRET` (the complete string, not a hex-decoded suffix). For example register `https://YOUR-INTEGRATION-HOST/webhooks/dawilo`, not a localhost address. Place a TLS reverse proxy in front of the loopback listener:

```sh
python3 bridge.py serve --config config.json --port 8790
python3 bridge.py events --config config.json
```

Proxy only `/webhooks/dawilo`, preserve the raw request body and `X-Dawilo-Signature`, limit bodies to 64 KiB, and forward to `127.0.0.1:8790`. Dawilo's dispatch scheduler must run; registering a URL alone does not send events. Use the sandbox to verify an actual signed delivery.

The receiver verifies HMAC on raw bytes, a five-minute timestamp window, API version, environment, event type and the configured resource allowlist. It commits to the inbox before returning 204, deduplicates by event ID, rejects different content under the same ID, and returns 503 on storage failure. Signed events for unexpected resources are rejected. Set `webhook_resources` explicitly when the key handles multiple establishments.

`events` lists unprocessed metadata. Your vendor adapter should process an event in a vendor-supported transaction and record its ID in a unique receipt table **in the same transaction**. Only then mark the bridge receipt processed:

```sh
python3 bridge.py ack --config config.json --event-id EVENT_UUID
```

Without a completed vendor adapter, events stay pending in the inbox; no vendor database is changed. No automatic mark-as-processed stub is supplied. Reordered events need version checks or an authorized lookup of current state. Appointment revisions are opaque equality tokens, not sortable numbers. Webhooks do not contain patient names/phone/clinical text; their metadata alone cannot create a complete patient chart in another system. Only `appointment.created`, `prescription.dispensed`, and `referral.completed` exist. There are no update/cancel event feeds or historical backfills.

Coordinate signing-secret rotation with receiver deployment; registration changes cancel Dawilo's old queued deliveries and do not recall deliveries already received. Bearer-key rotation also requires preserving import mappings as described above.

## Other integration paths

Any supported programming language can call the versioned HTTPS/JSON API and verify HMAC-SHA256; this Python starter is optional. For MySQL, PostgreSQL, SQL Server, Access or SQLite, ask the vendor for a read-only view/export and map its fields, never assume table names. Database engine similarity is not application compatibility. For a vendor REST/FHIR/HL7 interface, write and test a dedicated adapter. Dawilo does not expose native FHIR, HL7 v2/MLLP, CDA or DICOM endpoints in this release.

Pharmacy POS and lab adapters can use the existing QR-aware prescription/lab endpoints from `partner-openapi.json`; the CSV appointment importer does not dispense medicines or publish lab results. Encryption, patient sharing, revision checks, idempotency, payload limits and resource authorization remain enforced server-side.

Use `docs/INTEGRATION-COMPATIBILITY.md` and `docs/PARTNER-API.md` from the source package for the support matrix and acceptance checklist. No live vendor connection is implied by installing this starter.
