Contract Management
Contract Management
DELPHOS manages health insurance contracts with temporal pricing — every price adjustment (reajuste) creates a new contract period, ensuring events are always billed at the configuration valid on the event date, not the current date.
Key Concepts
| Concept | Description |
|---|---|
| Convenio | A master insurance plan (Unimed, Amil, SUS, or self-pay). Created once per payer. |
| Contract Period | A time-bounded pricing configuration within a convenio. Contains CBHPM version, percentage, overrides, and copay rules. |
| Reajuste | A price adjustment that closes the current period and creates a new one, preserving the historical record. |
| Snapshot | A point-in-time view of the complete pricing configuration for audit and billing. |
| Override | A procedure-specific price that takes priority over the CBHPM table price. |
Convenio (Insurance Plan) CRUD
Create a convenio
POST /v1/scheduling/convenios| Field | Type | Required | Default | Description |
|---|---|---|---|---|
name | string | Yes | — | Display name (max 255 chars) |
ans_code | string | No | null | ANS registration code (unique per tenant) |
payment_type | string | Yes | — | "particular", "convenio", or "sus" |
cbhpm_table_version | string | No | null | CBHPM table version (max 10 chars) |
requires_card_number | boolean | No | false | Whether patient must present insurance card |
requires_authorization | boolean | No | false | Whether prior authorization is required |
copay_enabled | boolean | No | false | Whether copay (coparticipacao) applies |
default_copay_percent | decimal | No | null | Default copay percentage (0-100) |
default_copay_max_cents | integer | No | null | Maximum copay in centavos |
contact_info | object | No | null | Structured contact details |
integration_code | string | No | null | External billing system code |
notes | string | No | null | Administrative notes |
| Code | Meaning |
|---|---|
201 | Convenio created |
409 | Duplicate name or ANS code |
422 | Validation error |
curl -X POST "https://your-instance.delphos.app/v1/scheduling/convenios" \ -H "x-api-key: YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "name": "Unimed Rio", "ans_code": "393321", "payment_type": "convenio", "cbhpm_table_version": "2024", "requires_card_number": true, "copay_enabled": true, "default_copay_percent": 30, "default_copay_max_cents": 15000 }'import httpx
response = httpx.post( "https://your-instance.delphos.app/v1/scheduling/convenios", headers={"x-api-key": "YOUR_API_KEY"}, json={ "name": "Unimed Rio", "ans_code": "393321", "payment_type": "convenio", "cbhpm_table_version": "2024", "requires_card_number": True, "copay_enabled": True, "default_copay_percent": 30, "default_copay_max_cents": 15000, },)
convenio = response.json()print(f"Created convenio: {convenio['id']}")List convenios
GET /v1/scheduling/convenios| Parameter | Type | Default | Description |
|---|---|---|---|
is_active | boolean | null | Filter by active status |
offset | integer | 0 | Pagination offset |
limit | integer | 50 | Records per page (max 200) |
curl "https://your-instance.delphos.app/v1/scheduling/convenios?is_active=true&limit=10" \ -H "x-api-key: YOUR_API_KEY"import httpx
response = httpx.get( "https://your-instance.delphos.app/v1/scheduling/convenios", headers={"x-api-key": "YOUR_API_KEY"}, params={"is_active": True, "limit": 10},)
data = response.json()for convenio in data["items"]: print(f"{convenio['name']} ({convenio['payment_type']})")Validate convenio eligibility
Before booking an appointment, verify that a provider accepts a specific convenio for the requested appointment type.
POST /v1/scheduling/convenio/validate| Field | Type | Required | Description |
|---|---|---|---|
provider_profile_id | UUID | Yes | Provider to check |
convenio_id | UUID | Yes | Convenio to validate |
appointment_type_id | UUID | Yes | Appointment type to verify |
| Code | Meaning |
|---|---|
200 | Eligibility result returned |
404 | Provider or convenio not found |
422 | Ineligible — convenio not accepted or appointment type not covered |
curl -X POST "https://your-instance.delphos.app/v1/scheduling/convenio/validate" \ -H "x-api-key: YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "provider_profile_id": "550e8400-e29b-41d4-a716-446655440001", "convenio_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890", "appointment_type_id": "b2c3d4e5-f6a7-8901-bcde-f12345678901" }'import httpx
response = httpx.post( "https://your-instance.delphos.app/v1/scheduling/convenio/validate", headers={"x-api-key": "YOUR_API_KEY"}, json={ "provider_profile_id": "550e8400-e29b-41d4-a716-446655440001", "convenio_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890", "appointment_type_id": "b2c3d4e5-f6a7-8901-bcde-f12345678901", },)
if response.status_code == 200: result = response.json() print(f"Eligible: {result['eligible']}")elif response.status_code == 422: print(f"Ineligible: {response.json()['detail']}")Provider-Convenio Linking
Providers must be explicitly linked to the convenios they accept.
Link a convenio to a provider
POST /v1/scheduling/providers/{provider_id}/convenios| Field | Type | Required | Description |
|---|---|---|---|
convenio_id | UUID | Yes | Convenio to link |
| Code | Meaning |
|---|---|
201 | Link created |
404 | Provider or convenio not found |
409 | Provider already accepts this convenio |
List provider convenios
GET /v1/scheduling/providers/{provider_id}/conveniosRemove a convenio from a provider
DELETE /v1/scheduling/providers/{provider_id}/convenios/{convenio_id}Performs a soft-delete — the link is deactivated, not permanently removed.
Particular Prices
Convenios with payment_type: "particular" (self-pay) can have a dedicated
price catalog. This catalog defines procedure-level prices for patients paying
out of pocket.
Create a particular price
POST /v1/scheduling/convenios/{convenio_id}/particular-prices| Field | Type | Required | Description |
|---|---|---|---|
procedure_code | string | Yes | Procedure identifier (e.g. TUSS code, max 50 chars) |
procedure_name | string | Yes | Human-readable description (max 500 chars) |
price_cents | integer | Yes | Price in centavos (>= 0) |
notes | string | No | Administrative notes |
metadata | object | No | Structured metadata |
| Code | Meaning |
|---|---|
201 | Price entry created |
404 | Convenio not found |
409 | Duplicate procedure code |
422 | Convenio is not particular type |
curl -X POST "https://your-instance.delphos.app/v1/scheduling/convenios/CONVENIO_ID/particular-prices" \ -H "x-api-key: YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "procedure_code": "10101012", "procedure_name": "Consulta em consultorio", "price_cents": 25000 }'import httpx
response = httpx.post( f"https://your-instance.delphos.app/v1/scheduling/convenios/{convenio_id}/particular-prices", headers={"x-api-key": "YOUR_API_KEY"}, json={ "procedure_code": "10101012", "procedure_name": "Consulta em consultorio", "price_cents": 25000, },)
price = response.json()print(f"Created price: {price['id']} - R$ {price['price_cents'] / 100:.2f}")List particular prices
GET /v1/scheduling/convenios/{convenio_id}/particular-prices| Parameter | Type | Default | Description |
|---|---|---|---|
include_inactive | boolean | false | Include soft-deleted entries |
search | string | null | Search by code or name (max 100 chars) |
offset | integer | 0 | Pagination offset |
limit | integer | 50 | Records per page (max 200) |
Update a particular price
PUT /v1/scheduling/convenios/{convenio_id}/particular-prices/{price_id}Full replacement (PUT semantics). All fields are required.
Delete a particular price
DELETE /v1/scheduling/convenios/{convenio_id}/particular-prices/{price_id}Soft-delete — the record is deactivated, not permanently removed.
Bulk create
POST /v1/scheduling/convenios/{convenio_id}/particular-prices/bulkCreate up to 100 prices in a single transaction. Duplicates within the batch are skipped and reported in the response summary.
| Field | Type | Required | Description |
|---|---|---|---|
items | array | Yes | List of price objects (max 100) |
Response:
{ "created": [{ "id": "...", "procedure_code": "10101012", "price_cents": 25000, ... }], "created_count": 8, "skipped_count": 2, "skipped_codes": ["10101012", "10101039"]}Contract Periods
Contract periods are time-bounded pricing configurations within a convenio.
Each period records a CBHPM table version, percentage, and optional procedure
overrides. Periods follow a lifecycle: draft -> active -> closed (manual)
or expired (automatic, via lifecycle processing).
Create a contract period
POST /v1/scheduling/convenios/{convenio_id}/contracts| Field | Type | Required | Default | Description |
|---|---|---|---|---|
start_date | date | Yes | — | Validity start date |
end_date | date | No | null | Validity end date (null = open-ended) |
cbhpm_table_version | string | No | null | CBHPM table version (max 10 chars) |
cbhpm_percentage | decimal | No | null | CBHPM percentage (0-100) |
notes | string | No | null | Administrative notes |
metadata | object | No | null | Structured metadata |
New periods are created in draft status. end_date must be strictly after
start_date when provided.
| Code | Meaning |
|---|---|
201 | Period created in draft status |
404 | Convenio not found |
409 | Overlapping period dates |
422 | Validation error (e.g. end_date before start_date) |
curl -X POST "https://your-instance.delphos.app/v1/scheduling/convenios/CONVENIO_ID/contracts" \ -H "x-api-key: YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "start_date": "2026-01-01", "end_date": "2026-12-31", "cbhpm_table_version": "2024", "cbhpm_percentage": 70, "notes": "Annual contract 2026" }'import httpx
response = httpx.post( f"https://your-instance.delphos.app/v1/scheduling/convenios/{convenio_id}/contracts", headers={"x-api-key": "YOUR_API_KEY"}, json={ "start_date": "2026-01-01", "end_date": "2026-12-31", "cbhpm_table_version": "2024", "cbhpm_percentage": 70, "notes": "Annual contract 2026", },)
period = response.json()print(f"Draft period created: {period['id']}, status: {period['status']}")List contract periods
GET /v1/scheduling/convenios/{convenio_id}/contracts| Parameter | Type | Default | Description |
|---|---|---|---|
status | string | null | Filter by status: draft, active, terminated, expired, renewed |
include_inactive | boolean | false | Include soft-deleted periods |
offset | integer | 0 | Pagination offset |
limit | integer | 50 | Records per page (max 200) |
Get a specific period
GET /v1/scheduling/convenios/{convenio_id}/contracts/{period_id}Get period active on a specific date
GET /v1/scheduling/convenios/{convenio_id}/contracts/at-date| Parameter | Type | Required | Description |
|---|---|---|---|
date | date | Yes | The reference date to look up |
This endpoint returns the contract period whose validity range contains the given date — critical for temporal pricing queries.
Update a draft period
PUT /v1/scheduling/convenios/{convenio_id}/contracts/{period_id}Only periods in draft status can be updated (full replacement semantics).
Active, terminated, and expired periods are immutable.
Delete a draft period
DELETE /v1/scheduling/convenios/{convenio_id}/contracts/{period_id}Only draft periods can be deleted.
Period Lifecycle Transitions
Activate a period
PATCH /v1/scheduling/convenios/{convenio_id}/contracts/{period_id}/activateTransitions a draft period to active status. Once active, the period
becomes immutable and is used for pricing calculations.
| Code | Meaning |
|---|---|
200 | Period activated |
404 | Period not found |
422 | Invalid state (not in draft) |
Terminate a period
PATCH /v1/scheduling/convenios/{convenio_id}/contracts/{period_id}/terminateTransitions an active period to terminated status. The period remains
queryable for historical pricing but no longer applies to new events.
Reajuste (Price Adjustment)
A reajuste closes the current period and creates a new one, optionally applying a percentage adjustment to all prices. Configuration from the old period (overrides, particular prices, type rules) can be copied to the new one.
POST /v1/scheduling/convenios/{convenio_id}/contracts/{period_id}/reajuste| Field | Type | Required | Default | Description |
|---|---|---|---|---|
new_start_date | date | Yes | — | Start date for the new period |
new_end_date | date | No | null | End date (null = open-ended) |
cbhpm_table_version | string | No | null | New CBHPM version |
cbhpm_percentage | decimal | No | null | New CBHPM percentage (0-100) |
reajuste_percent | decimal | No | null | Price adjustment (-100 to 1000) |
reajuste_description | string | No | null | Description (e.g. “Annual 2026 - IGPM + 2%“) |
copy_overrides | boolean | No | true | Copy procedure overrides from previous period |
copy_particular_prices | boolean | No | true | Copy particular prices |
copy_type_rules | boolean | No | true | Copy appointment type rules |
notes | string | No | null | Notes for the new period |
metadata | object | No | null | Additional metadata |
Response includes the new period plus a copy summary:
{ "new_period": { "id": "...", "status": "active", "validity_start": "2026-07-01" }, "closed_period_id": "...", "overrides_copied": 12, "particular_prices_copied": 5, "type_rules_copied": 3}curl -X POST "https://your-instance.delphos.app/v1/scheduling/convenios/CONVENIO_ID/contracts/PERIOD_ID/reajuste" \ -H "x-api-key: YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "new_start_date": "2026-07-01", "new_end_date": "2027-06-30", "cbhpm_table_version": "2025", "cbhpm_percentage": 75, "reajuste_percent": 8.5, "reajuste_description": "Annual 2026 - IGPM + 2%", "copy_overrides": true, "copy_particular_prices": true, "copy_type_rules": true }'import httpx
response = httpx.post( f"https://your-instance.delphos.app/v1/scheduling/convenios/{convenio_id}/contracts/{period_id}/reajuste", headers={"x-api-key": "YOUR_API_KEY"}, json={ "new_start_date": "2026-07-01", "new_end_date": "2027-06-30", "cbhpm_table_version": "2025", "cbhpm_percentage": 75, "reajuste_percent": 8.5, "reajuste_description": "Annual 2026 - IGPM + 2%", "copy_overrides": True, "copy_particular_prices": True, "copy_type_rules": True, },)
result = response.json()print(f"New period: {result['new_period']['id']}")print(f"Closed period: {result['closed_period_id']}")print(f"Overrides copied: {result['overrides_copied']}")Snapshots
Retrieve a point-in-time view of a contract period’s complete configuration, including all overrides, particular prices, and type rules.
GET /v1/scheduling/convenios/{convenio_id}/contracts/{period_id}/snapshotSnapshots are read-only and useful for billing audits and dispute resolution.
Response fields:
| Field | Type | Description |
|---|---|---|
period_id | UUID | Contract period UUID |
convenio_id | UUID | Parent convenio UUID |
event_date | date | Reference date for the snapshot |
snapshot_generated_at | datetime | When the snapshot was generated |
validity_start | date | Period validity start date |
validity_end | date | Period validity end date (null if open-ended) |
cbhpm_table_version | string | CBHPM table version |
cbhpm_percentage | decimal | CBHPM percentage |
status | string | Period status at snapshot time |
reajuste_percent | decimal | Applied adjustment percentage |
overrides | array | All procedure override records |
particular_prices | array | All particular price records |
type_rules | array | All appointment type rule records |
Change Log
Every mutation to a contract period or its child records is recorded in an immutable audit log.
GET /v1/scheduling/convenios/{convenio_id}/contracts/{period_id}/changelogEach entry contains:
| Field | Type | Description |
|---|---|---|
change_type | string | create, update, activate, reajuste, etc. |
change_description | string | Human-readable description |
changed_by_user_id | UUID | Who made the change |
previous_values | object | Values before the change |
new_values | object | Values after the change |
affected_table | string | Database table affected |
affected_record_count | integer | Number of records affected |
Contract Lifecycle Automation
DELPHOS automates contract lifecycle management through a processing endpoint that handles auto-expiration and notification generation.
Run lifecycle processing
POST /v1/scheduling/contracts/lifecycle/processThis idempotent endpoint:
- Expires contract periods whose
validity_endhas passed (transitions fromactivetoexpired). - Generates notifications for periods approaching expiration (30-day and 7-day warnings).
Response:
{ "expiration": { "expired_count": 2, "expired_period_ids": ["...", "..."], "processed_at": "2026-04-14T10:00:00Z" }, "notification": { "notifications_created": 3, "notifications_by_type": { "expiry_30d": 2, "expiry_7d": 1 }, "processed_at": "2026-04-14T10:00:00Z" }, "processed_at": "2026-04-14T10:00:00Z"}List lifecycle notifications
GET /v1/scheduling/contracts/notifications| Parameter | Type | Default | Description |
|---|---|---|---|
convenio_id | UUID | null | Filter by convenio |
offset | integer | 0 | Pagination offset |
limit | integer | 50 | Records per page (max 200) |
Mark notification as read
PATCH /v1/scheduling/contracts/notifications/{notification_id}/readRenew an expiring contract
POST /v1/scheduling/convenios/{convenio_id}/contracts/{period_id}/renewCreates a draft renewal from an existing period, copying all configuration.
| Field | Type | Required | Default | Description |
|---|---|---|---|---|
new_end_date | date | No | null | End date for renewal (null = open-ended) |
cbhpm_table_version | string | No | null | Override CBHPM version (copies from source if null) |
cbhpm_percentage | float | No | null | Override percentage (copies from source if null) |
notes | string | No | null | Administrative notes |
metadata | object | No | null | Structured metadata |
Response includes the new period UUID and a copy summary:
| Field | Type | Description |
|---|---|---|
new_period_id | UUID | Newly created draft period |
source_period_id | UUID | Source period used as template |
convenio_id | UUID | Parent convenio UUID |
validity_start | date | Start date of the new period |
validity_end | date | End date (null if open-ended) |
cbhpm_table_version | string | CBHPM table version of the new period |
cbhpm_percentage | float | CBHPM percentage of the new period |
overrides_copied | integer | Number of overrides copied |
particular_prices_copied | integer | Number of prices copied |
type_rules_copied | integer | Number of type rules copied |
status | string | Always "draft" |
created_at | datetime | When the new period was created |
Error Handling
All contract endpoints return structured error responses:
{ "detail": "Contract period not found: a1b2c3d4-..."}| Code | When |
|---|---|
404 | Convenio or period not found |
409 | Overlapping periods or duplicate convenio |
422 | Invalid state transition or validation error |
500 | Internal server error |