Manage configuration in code
Keep a project's configuration in your repository, and change it the way you change code: review the differences, then deploy them. Everything here is also in the portal, and a change made in one place shows in the other.
There are two ways to do it:
- The
calmlensCLI reads one file per project, prints the differences and makes them. - The configuration API and the SDKs'
uploadPresets,projectSettingsandmetadataFieldsmethods, for your own deploy scripts.
The configuration file
Add calmlens.config.json to your repository. Each project has its own file; use --config to choose one.
{
"policies": {
"Community rules": { "presetId": "general-community" }
},
"policy": "Community rules",
"uploadPresets": {
"profile-photos": {
"name": "Profile photos",
"allowedContentTypes": ["image/*"],
"maxSizeInBytes": 10000000,
"visibility": "public",
"revealDecision": true,
"allowedOrigins": ["https://example.com"]
},
"feedback-form": {
"name": "Feedback form",
"access": "anyone",
"dailyUploadLimit": 20,
"policy": "Community rules"
}
},
"lenses": {
"card": { "name": "Card", "transform": { "width": 640, "format": "webp" } }
},
"metadataFields": {
"userId": { "title": "User", "type": "string", "icon": "user", "color": "#2563eb" }
},
"settings": {
"blockUnmoderatedAssets": true,
"optOutJurisdictions": ["EU"]
}
}A section you leave out isn't managed, so you can start with one section and add others later.
| Section | Items are named by | What the file sets |
|---|---|---|
policies | The policy's name | presetId to create it as a copy of a built-in policy, settings for its complete settings, and description. A policy that exists already is matched by name, and only its settings and description are compared. |
policy | A policy's name | The policy the project moderates with. It can be in policies or be any of the organisation's policies. |
uploadPresets | The preset's key | The preset's whole definition. Fields you leave out take their defaults. policy names a policy instead of giving its ID. |
lenses | The lens's identifier | The lens's whole definition. type defaults to image and active to true. |
metadataFields | The field's ID | The field's title, type, icon and colour. |
settings | The setting's name | Only the settings you list. Others keep their values. See the project settings object. |
Plan and apply
calmlens plan compares the file with the project, prints the differences and changes nothing.
calmlens plan + policy "Community rules"
~ project policy "Community rules"
~ upload preset "profile-photos" (allowedOrigins)
+ lens "card"
2 to create, 2 to update, 0 to delete, 3 unchanged.calmlens apply makes those changes.
calmlens applyapply reads the project again and sends each change with the revision it read. If someone changes an item in the portal or another deploy changes it first, that change is refused with upload-preset/revision-conflict, project-settings/revision-conflict or policy/revision-conflict rather than overwritten. Run calmlens plan to see the latest differences, then decide which to keep. apply stops at the first refusal, and changes made before it stay, so running it again finishes the rest.
Delete what the file doesn't list
By default nothing is deleted. Add --prune to delete upload presets, lenses and metadata fields that the file's section doesn't list. Check the plan first:
calmlens plan --pruneA section you leave out is never pruned. Policies are never pruned, because other projects in the organisation can use them. Delete a policy in the portal or with the policies API.
What can't be set from the file
- Through a project's key you can change only the policies no other project uses. A policy that another project uses is refused with
policy/used-by-another-project, so change it in the portal. - Turning on
aiMediaSearchEnabledis billed for indexing the project's existing media, so it needs a budget you agree to. Turn it on in the portal or with Update Project Settings andsearchIndexingBudgetUnits, then list it in the file.
Deploy from CI
Create a key for your deploys in Portal › API keys with the Configurator role. It can manage this project's configuration, but not its members, API keys or connectors. A custom key needs only the write permission for each section your file lists: policies:write, upload-presets:write, lenses:write, metadata:write or settings:write. plan reads only those sections, so a key that only plans needs just the matching :read permissions.
Store the key as a secret, check the plan on pull requests and apply it on your main branch. For GitHub Actions:
name: CalmLens configuration
on:
pull_request:
paths: [calmlens.config.json]
push:
branches: [main]
paths: [calmlens.config.json]
env:
CALMLENS_API_KEY: ${{ secrets.CALMLENS_CONFIG_API_KEY }}
CALMLENS_PROJECT_ID: ${{ vars.CALMLENS_PROJECT_ID }}
jobs:
configuration:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- run: npx calmlens plan
- if: github.event_name == 'push'
run: npx calmlens applyUse a separate project and key for each environment, each with its own file or the same one.
From your own code
The SDKs have the same operations, for deploy scripts of your own. uploadPresets.ensure creates or replaces presets by key and returns them with their revisions, projectSettings.get and update read and replace the settings, and metadataFields.ensure creates or replaces fields by ID. The configuration API reference shows each endpoint in every SDK.