Skip to main content
Profiles are the schema contracts that govern resources under a source. They specify which properties exist, which data types they use, and which validation rules or vocabularies apply. Every resource instance references a profile key, so profiles are the backbone of data quality in the registry.

Scope and keys

  • Per-source uniqueness — Profile keys are unique within a source. Two sources can reuse the same key.
  • API usage — You reference a profile key when writing resources, defining relationships, configuring partitions, or declaring pipelines.
  • Versioning — Updating a profile changes which data future writes must respect. Existing records remain untouched until revalidated through a pipeline or reingested.

Profile types

Profiles come in two flavors:
  • ROOT — Represents a top-level resource that exists independently. It becomes its own collection inside the source and can be exposed directly through partitions.
  • CONTAINED — Represents a component that only lives inside a parent resource. It is written, versioned, and deleted as part of the parent’s lifecycle.
Use contained profiles when the data cannot stand alone but still requires dedicated structure or validation—like opening hours for a clinic or insurance contracts attached to a provider.

Properties and validation

Each property declares:
  • A type (symbol, integer, address, custom objects, arrays…)
  • Optional rules for validation (required, min/max, enum, pattern, vocabulary bindings)
  • Whether it can repeat (array) or embed structured objects (object)
Profiles integrate tightly with field validation so that ingestion fails fast when data violates the contract.

Contained resources

Contained profiles let you model complex data without multiplying top-level resources.

When to use contained profiles

  • The component depends entirely on a single parent resource.
  • You need to update the component independently from other properties of the parent.
  • You must allow multiple instances (for example, a clinic with many insurance contracts).

When not to use contained profiles

  • The component should be discoverable on its own (queryable partition collection).
  • Multiple parent resources must reference the same record.
  • You need graph relationships to or from that component.
In those cases, create a ROOT profile and connect records with relationships instead.

Example profile

curl -X PUT "https://$CLINIA_WORKSPACE/sources/clinics/v1/profiles/clinic" \
  -H "X-Clinia-API-Key: $CLINIA_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
  "type": "ROOT",
  "description": "Clinic profile",
  "properties": {
    "address": {
      "type": "address",
      "properties": {
        "type": {
          "type": "code",
          "rules": [
            {
              "enum": [
                "postal",
                "physical",
                "both"
              ]
            }
          ]
        }
      }
    }
  },
  "contained": {
    "weeklyHoursOfOperation": {
      "profileKey": "weekly-hours-of-operation",
      "cardinality": "1:1"
    }
  }
}'
This profile:
  • Uses the Clinia address complex type while layering custom validation.
  • Embeds a contained profile to track opening hours without creating an independent resource.
  • Keeps ingestion strict by validating enums and required fields.
Define contained profiles the same way—set "type": "CONTAINED" and describe their properties. They become embeddable components that the parent profile references under contained.
I