## Summary
- add packages/twenty-apps/rollup-engine: a parameterised rollup engine
that ships a default Opportunity → Company
aggregation.
- declare runtime config in package.json (TWENTY_API_KEY, optional
TWENTY_API_BASE_URL, and ROLLUP_ENGINE_CONFIG) so
app configuration lives entirely in Settings → Apps → [App] →
Configuration.
- document the workflow in README.md: deploy via twenty app sync,
populate env vars in the UI, use the Test panel with
a ready JSON payload, and reference troubleshooting tips.
- adjust the function entry point to a named async export and add
fallback logic for blank base URLs, matching the
UI’s env behaviour.
- prune legacy templates and examples so
config/templates/opportunity-to-company.json is the single copy/paste
starting point.
## UI / UX impact
After syncing the app:
- the Configuration screen shows the three env keys with helpful
descriptions (API key, optional base URL, JSON
override),
- the built-in Test your function panel works immediately
- and the default JSON config is available from
config/templates/opportunity-to-company.json for users who need to
customise rollups.
## Testing
- Out-of-the-box deploy to the hosted workspace (Opportunity update) ✓
- “Test your function” with the default config ✓
- Override example: point debugOpportunityCount at a scratch field via
ROLLUP_ENGINE_CONFIG ✓
- Optional: local smoke test (yarn install && yarn smoke) still passes
Rollup engine
General-purpose rollup engine for Twenty workspaces. The bundle ships a serverless function that materialises aggregations from a child object onto a parent object. The default configuration targets Opportunity → Company rollups.
Requirements
twenty-clinpm install -g twenty-cli- an API key with access to your workspace. Generate one at
https://twenty.com/settings/api-webhooks. - Node 20+ (only required if you plan to run the optional smoke test locally).
Metadata prerequisites
- Opportunity is a standard object, no extra provisioning required.
- Add these fields to the
companyobject (API names shown):totalPipelineAmount(Currency)totalOpportunityCount(Number)wonPipelineAmount(Currency)wonOpportunityCount(Number)openPipelineAmount(Currency)openOpportunityCount(Number)lastOpportunityCloseDate(Date)
- To script the setup, export
TWENTY_API_KEY(andTWENTY_METADATA_BASE_URLif you are not targetinghttp://localhost:3000/rest/metadata) and runyarn setup:metadata. The helper script calls the Metadata API to ensure each field exists on the Company object, skipping anything already provisioned.
Create the fields before syncing so PATCH requests succeed.
Quick start
-
Deploy the app
twenty auth login cd rollup-engine twenty app sync -
Configure environment variables
- Open Settings → Apps → Rollup engine → Configuration.
- Provide values for the manifest-defined keys:
TWENTY_API_KEY(required secret; create it in Twenty → Settings → API & Webhooks).TWENTY_API_BASE_URL(optional; leave blank to usehttps://app.twenty.com/rest).ROLLUP_ENGINE_CONFIG(optional JSON override; leave blank to use the baked-in config).
- Save the configuration. Changes are applied immediately—no redeploy required.
-
Verify via the Test panel
-
Still on the app page, select Test.
-
Paste a JSON payload with the company ID you want to validate:
{ "trigger": { "type": "databaseEvent", "eventName": "opportunity.updated" }, "record": { "companyId": "YOUR_COMPANY_ID" }, "opportunity": { "companyId": "YOUR_COMPANY_ID" } } -
Click Run. You should see a success summary and the company’s rollup fields updating.
-
-
Trigger live rollups
- Any Opportunity create/update/delete (or the nightly cron) now feeds into the Company metrics.
Runtime configuration
serverlessFunctions/calculaterollups/src/rollupConfig.tsholds the baked-in defaults. Override them by settingROLLUP_ENGINE_CONFIG(aliases:ROLLUPS_CONFIG,CALCULATE_ROLLUPS_CONFIG) to a JSON array in the Configuration UI.config/templates/opportunity-to-company.jsonmatches the default rollups shipped with the bundle. Copy it, tweak the object/field names you need, and paste the edited JSON intoROLLUP_ENGINE_CONFIG.filters[].dynamicValuecurrently supports"startOfYear"(UTC midnight on the first day of the current calendar year).
Default Opportunity → Company rollups
| Parent field | Suggested type | Description |
|---|---|---|
totalPipelineAmount |
Currency (amountMicros, currencyCode) |
Sum of positive-valued Opportunities linked to the Company. |
totalOpportunityCount |
Number | Count of Opportunities linked to the Company. |
wonPipelineAmount |
Currency (amountMicros, currencyCode) |
Sum of Opportunities where stage === "CUSTOMER". |
wonOpportunityCount |
Number | Count of Opportunities where stage === "CUSTOMER". |
openPipelineAmount |
Currency (amountMicros, currencyCode) |
Sum of Opportunities where stage !== "CUSTOMER". |
openOpportunityCount |
Number | Count of Opportunities where stage !== "CUSTOMER". |
lastOpportunityCloseDate |
Date | Most recent closeDate (ISO date). |
Assumptions:
- Opportunities expose
companyId,amount.amountMicros,amount.currencyCode,stage, andcloseDate. Adjust filters or fields as needed via the JSON config. - Amount totals are stored in micros (integers) to align with Twenty’s composite currency fields.
Optional local smoke test
Run this if you want to exercise the aggregation logic without calling the live API.
cd rollup-engine
yarn install
yarn smoke
The smoke script replaces fetch with an in-memory mock, executes the rollup function using sample
Opportunity data, and asserts the PATCH payload that would be sent to Twenty.
Troubleshooting
Test your functionreportsmainFile.main is not a function: delete the app (twenty app delete) and sync it again (twenty app sync) to clear any stale bundle.status: "error", message: "Unable to parse rollup configuration override": confirm thatROLLUP_ENGINE_CONFIGcontains valid JSON or leave it blank to fall back to the baked-in defaults.- No company updates after configuring everything: double-check the Company field API names,
confirm the Opportunity records have
amount.amountMicros > 0, and use the Test panel to inspect the summary details/logs.