Settings Schema (v1)

Your listing declares a settings form that Dentolize renders natively in the clinic UI (web now, mobile later), Arabic and English included. Your service reads the saved values from GET /api/v1/installation and receives plugin.settings.updated webhooks on change.

With the settings:write scope your service can also update its own settings via PATCH /api/v1/installation/settings (SDK: client.installation.updateSettings(values)) — useful for sync cursors or connection state. The body is the full settings object, validated against the same schema as UI saves; submit the "__SET__" sentinel to keep a stored secret. The PATCH response returns secrets masked and the change fires plugin.settings.updated. (GET /installation still returns real secret values — your service needs them to function; only clinic-UI reads are masked.)

The contract is defined (types + validators) in @dentolize/plugin-sdk — build your schema with defineSettingsSchema() to validate locally before submission.

{
  "version": 1,
  "sections": [
    {
      "key": "reminders",
      "title": { "en": "Reminder settings", "ar": "إعدادات التذكير" },
      "fields": [
        {
          "key": "hoursBefore",                       // ^[a-zA-Z][a-zA-Z0-9_]{0,63}$, unique across schema
          "type": "number",                            // text | secret | number | boolean | select | multiselect | url
          "label": { "en": "Hours before", "ar": "ساعات قبل الموعد" },
          "help": { "en": "…", "ar": "…" },            // optional
          "required": true,
          "default": 24,
          "validation": { "min": 1, "max": 168 },      // minLength/maxLength/pattern for strings
          "visibleIf": { "field": "enabled", "equals": true }   // one flat condition, no chaining
        }
      ]
    }
  ]
}

Rules