Skip to main content

Aventora CRM — complete guide

Single reference for aventora-crm (Aventora CRM): deployment, upgrade, provisioning/SSO, industry presets, workspace color themes, Sales Cockpit, TIPS sync, and troubleshooting.

Audience: operators, integrators, client backend developers, sales ops — not the CRM end-user training path.

DocumentWho it is for
CRM User GuideEnd users: people, pipeline, cockpit, campaigns, Engage, calendar booking
CRM video tutorialsEnd users: short walkthrough videos for the same daily work
This complete guideAdministrators and operators: deployment, environment, provisioning, CLI
Laravel IntegrationPartner/host technical integration
Gmail IMAP SetupMailbox connection for administrators

Laravel custom integration (no plugin): Laravel Integration — provisioning, SSO, embedded navigation, avatar.

Related platform docs: API Security Model, Platform API Keys, CRM MCP, Engagement Hub MCP, Outbound Email Templates, Gmail IMAP Setup


Table of contents​

  1. Concepts
  2. Environment variables
  3. Deployment and upgrade
  4. Admin CLI commands
  5. Provisioning and SSO
  6. Workspaces, users, and industry presets
  7. Sales Cockpit
  8. Campaign landing pages
  9. Demo workspace provisioning
  10. TIPS Services sync
  11. Laravel plugin
  12. Contacts and workspace API keys
  13. MCP (Cursor / Claude)
  14. Webhooks
  15. Troubleshooting

Concepts​

ConceptCRM meaningAPI surface
UserPerson who can log in to a workspacePOST /auth/provision/* on apex CRM_API_URL
Contactperson record in the People modulePOST /rest/people on workspace host with workspace API key

Provisioning and SSO use the apex server. Contact writes use https://{subdomain}.{crm-host}. Do not send /rest/people with the provisioning secret.


Environment variables​

aventora-crm (server)​

VariableRequiredDescription
PROVISIONING_SECRETYesBearer token for /auth/provision/* (server-side only)
APP_VERSIONYesValid semver (e.g. 1.20.0); drives workspace migrations. Never leave empty.
FRONTEND_URLMulti-workspaceApex URL, e.g. https://crm.aventora.ai
SERVER_URLYesAPI base; usually same as FRONTEND_URL
IS_MULTIWORKSPACE_ENABLEDProductiontrue for {subdomain}.crm.aventora.ai
AVENTORA_BASE_URLHub integrationEngagement Hub API base URL
AVENTORA_WEBHOOK_SECRETRecommendedValidates X-Aventora-Webhook-Secret on inbound webhooks
AVENTORA_HUB_AUTO_PROVISION_ENABLEDOptionalWhen true, enqueue Hub/domain provisioning after CRM workspace or member provisioning
AVENTORA_HUB_PROVISION_CALLBACK_URLWith auto-provisionAssistant URL, e.g. http://hub:8010/internal/crm-hub-provision
AVENTORA_HUB_PROVISION_CALLBACK_SECRETWith auto-provisionShared Bearer secret with Assistant (CRM_HUB_PROVISION_CALLBACK_SECRET)
DEFAULT_PHONE_COUNTRY_CODEOptionalISO 3166-1 alpha-2 (e.g. US) used for new/empty phone inputs when workspace Settings → Data Model → Phones → Default Country Code is unset. Calling code is derived via libphonenumber. Unset keeps prior behavior (no default).
SHOULD_PREFILL_STANDARD_OBJECTSOptionalDefault false. When true, workspace activation seeds Twenty demo CRM records (sample companies, people, opportunities, Quick Lead workflow, My First Dashboard). Leave unset/false for empty provisioned workspaces. When unset/false, Docker startup also runs workspace:remove-standard-objects-prefill to soft-delete those fixed seed UUIDs from existing workspaces.
WHITE_LABELOptionalPartner display name for UI labels, API toasts/errors, and transactional emails. Unset or blank → Aventora (product name {name} CRM). Example: WHITE_LABEL=Acme → “Acme” / “Acme CRM”. Logos and website URLs are unchanged. Pass through Docker Compose from .env. Existing workspaces keep previously seeded role/workflow/tab names in the DB until recreated or manually renamed.
USE_2FAOptionalDefault true. When true, workspace admins (and platform admins) must enable and enter 2FA at login. When false, CRM does not enforce admin 2FA enrollment or verification (workspace Settings → Security 2FA toggle is also ignored at login). Compose passes this into server/worker (${USE_2FA:-true}).

Per workspace (not env): AVENTORA_API_KEY in Settings → Applications → Aventora Phone. AVENTORA_PERSON_WALL_ENABLED in Settings → Applications → Aventora (empty = inherit; false = opt out when PERSON_WALL=true).

Person wall (optional, server)​

VariableDefaultDescription
PERSON_WALLfalseKill switch; when true, members see only their own Person records unless workspace opts out
PERSON_WALL_WORKSPACE_IDSemptyComma-separated workspace UUID allowlist (empty = all workspaces)
PERSON_WALL_WORKSPACE_IDemptyDeprecated single-UUID allowlist
PERSON_WALL_EXCLUDE_SYSTEM_RECORDSfalseHide integration-created persons from scoped members
PERSON_WALL_SYSTEM_ACTOR_NAMEemptyDisplay name for system-created person createdBy. When empty, uses WHITE_LABEL (or Aventora).
PERSON_WALL_DEBUGfalseLog PersonWall scope decisions

Rollout: workspace:sync-aventora-variables, then workspace:backfill-person-created-by. See Person wall (optional, server).

Calling app (integrator / admin / domain-chatbot)​

VariableRequiredDescription
CRM_API_URLYesApex URL for /auth/provision/*
CRM_PROVISIONING_SECRETYesMust match CRM PROVISIONING_SECRET
CRM_PUBLIC_BASE_URLContacts / URLsDefaults to CRM_API_URL
CRM_WORKSPACE_API_KEYContact writesWorkspace-scoped token for /rest/*

Laravel uses AVENTORA_CRM_URL and AVENTORA_CRM_PROVISIONING_SECRET.

Host-controlled Compose env files (.env next to docker-compose.yml) must stay off Git. On the Linux VPS restrict the file to the deployment user, for example sudo chmod 600 .env. Typical documented path: /opt/aventora/crm/.env.

TIPS (optional, server + worker)​

VariableRequiredDefaultDescription
TIPS_CLIENT_IDFor sync—OAuth client id
TIPS_CLIENT_SECRETFor sync—OAuth secret
TIPS_API_BASE_URLNohttps://tipsadvisors.tipservices.ca/apiInclude /api
TIPS_SYNC_WORKSPACE_IDFor sync—Single workspace UUID
TIPS_SYNC_ENABLEDNofalseEnable scheduled cron
TIPS_SYNC_CRON_PATTERNNo0 * * * *BullMQ cron (hourly)

Deployment and upgrade​

Production (Docker)​

Production images are built by GitHub Actions for linux/amd64 and published to GitHub Container Registry as ghcr.io/<github-owner>/aventora-crm. latest is the most recent successful main build. Prefer the immutable sha-<commit-sha> tag for a specific deploy or rollback. Locally built images (especially on ARM machines) are not authoritative for production.

Containers run migrations and upgrade on startup unless DISABLE_DB_MIGRATIONS=true:

yarn command:prod cache:flush
yarn command:prod upgrade
yarn command:prod workspace:sync-aventora-sales-person-fields
yarn command:prod workspace:ensure-aventora-user-role-permissions
yarn command:prod workspace:backfill-provisioned-workspace-admins
yarn command:prod workspace:remove-standard-objects-prefill
yarn command:prod cache:flush

Manual upgrade after pulling a new image:

cd /path/to/compose # directory with docker-compose.yml and .env
docker compose pull
docker compose up -d --force-recreate server worker

# Or run explicitly:
docker compose exec server sh -c \
'yarn command:prod cache:flush && yarn command:prod upgrade && yarn command:prod cache:flush'
docker compose exec server yarn command:prod workspace:sync-aventora-variables

Health check:

curl -sf https://crm.example.com/healthz

Keep the worker container running for BullMQ (TIPS cron, outbound jobs).

Development​

npx nx run twenty-server:platform:upgrade

Equivalent manual steps: build → cache:flush → upgrade → workspace:sync-aventora-sales-person-fields → workspace:remove-standard-objects-prefill → cache:flush → workspace:sync-aventora-variables.

What upgrade does​

  1. Pending TypeORM core migrations
  2. Workspace-level data migrations for current APP_VERSION minor
  3. Updates core.workspace.version per workspace

Check workspace versions:

SELECT id, version, "displayName", "activationStatus"
FROM core.workspace
ORDER BY version NULLS FIRST;

New deploy checklist​

  1. Update .env (APP_VERSION, URLs, secrets)
  2. docker compose up -d (server runs migrations + upgrade + cron register; first listen can take 3–4 minutes — healthcheck start_period is 6 minutes)
  3. Confirm curl …/healthz and worker Up (not Created)
  4. workspace:sync-aventora-variables
  5. Existing workspaces with an industry preset: workspace:reapply-industry-preset (seeds new [preset:…] workflows and cockpit rules without changing preset id/profile)
  6. Review Settings → Workflows and Sales Cockpit → Automation Suggestions; activate templates you want live

Further deployment paths are available to customers under agreement. Contact sales@aventora.ai.


Admin CLI commands​

Run in production:

docker compose exec server yarn command:prod <command> [options]

Dev:

npx nx run twenty-server:command -- <command> [options]

Quick index​

All entries below are command names. In Docker/prod, run them as yarn command:prod <command-name>.

CommandPurpose
upgradePrimary migrator — run on every deploy
cache:flushFlush Redis; run around upgrade
workspace:sync-aventora-variablesEnsure Aventora app vars on all workspaces (non-destructive)
workspace:sync-aventora-sales-person-fieldsSales Cockpit Person fields (auto after upgrade in Docker)
workspace:remove-standard-objects-prefillSoft-delete Twenty demo seed rows (fixed UUIDs only) from active/suspended workspaces; skipped when SHOULD_PREFILL_STANDARD_OBJECTS=true unless --force. Auto on Docker startup. Use --destroy for permanent removal.
workspace:seed-aventora-sales-cockpit-demoDemo signals on existing people
workspace:export-industry-presetExport a catalog preset as partner-editable JSON (optional --format csv fields sheet). --all --out-dir dumps every industry default profile
workspace:apply-industry-presetApply a catalog preset, or a partner JSON file (--from-file), to one workspace or every workspace
workspace:reapply-industry-presetRe-apply preset from each workspace’s stored industryPreset / industryProfile (catalog ids only)
tips:bootstrap-fieldsTIPS fields + hub role + RLS
tips:syncOne-off TIPS inbound sync
cron:register:allRegister background crons (incl. TIPS)
workspace:backfill-person-created-byBackfill person.createdBy for PERSON_WALL readiness
workspace:backfill-provisioned-workspace-adminsPromote the Laravel/TIPS workspace owner to Admin (auto on Docker startup)

Demo sales signals​

After upgrade and person-field sync:

docker compose exec server yarn command:prod workspace:seed-aventora-sales-cockpit-demo

Idempotent: adds demo signals on existing people in active workspaces. Does not create or delete CRM records.

Local dev (destructive full reset + demo signals):

npx nx database:reset twenty-server

Industry preset on existing workspace​

Re-apply after upgrade (uses each workspace’s stored preset; no UUID or preset flags required):

docker compose exec server yarn command:prod workspace:reapply-industry-preset

Optional single workspace: add -w <workspace-uuid>. Mostly additive (missing [preset:…] workflows, rules, fields). Engage defaults (AVENTORA_DEFAULT_CHANNEL / TYPE / INSTRUCTION) are overwritten from the preset.

First-time apply or change preset (requires workspace id; use --force if already applied):

docker compose exec server yarn command:prod workspace:apply-industry-preset \
-w <workspace-uuid> \
--industry-preset insurance \
--industry-profile general_insurance_advisor

Export a catalog preset for partners (JSON is the apply format; CSV is a review sheet only):

docker compose exec server yarn command:prod workspace:export-industry-preset \
--industry-preset insurance \
--industry-profile personal_lines \
--out /tmp/insurance-personal-lines.json

docker compose exec server yarn command:prod workspace:export-industry-preset \
--industry-preset generic \
--out /tmp/generic.json \
--format csv

--all --out-dir /tmp/presets writes one JSON per catalog industry using that industry’s default profile. --format csv also writes a .fields.csv beside each JSON (object,group,name,label,type,required). CSV cannot be applied.

Partners edit preset.industry (for example acme_dental), labels, fields, pipelines, workflows, and Engage defaults, then send the JSON back.

Apply a partner JSON file:

docker compose exec server yarn command:prod workspace:apply-industry-preset \
--from-file /tmp/acme.json -w <workspace-uuid> --force

docker compose exec server yarn command:prod workspace:apply-industry-preset \
--from-file /tmp/acme.json --force --all-workspaces

--from-file cannot be combined with --industry-preset / --industry-profile. Targeting every workspace requires both --force and --all-workspaces. File presets do not appear in onboarding or POST /auth/provision/*. If a workspace stores a custom industryPreset id that is not in the catalog, workspace:reapply-industry-preset fails and operators must re-apply with --from-file.

Check state:

SELECT id, "displayName", "industryPreset", "industryProfile", "presetAppliedAt"
FROM core.workspace WHERE id = '<uuid>';

Provisioning and SSO​

All routes: Authorization: Bearer <PROVISIONING_SECRET> on CRM_API_URL.

MethodPathPurpose
POST/auth/provision/resolveFind or create workspace + user; optional customSettings. Returns twentyApiKey when a new workspace is created
POST/auth/provision/rotate-api-keyRevoke Partner Provisioning keys and mint a new Admin JWT (twentyApiKey)
POST/auth/provision/login-tokenSSO token + workspaceUrl + workspaceOrigin; optional page, avatar, customSettings
POST/auth/provision/person-ownership-contextResolve workspace member + Person Wall scope for Hub contact creates
POST/auth/provision/userAdd user to existing workspace
POST/auth/provision/move-userMove user membership from one workspace to another (access only)
POST/auth/provision/workspaceCreate or reuse named workspace (supports industryPreset)
GET/auth/provision/workspace?subdomain= or ?displayName=Lookup workspace
GET/auth/provision/workspaces?activationStatus=ACTIVEList workspaces with hubConnected, adminEmail, assignedDomain
POST/auth/provision/link-workspaceWire Hub↔CRM; returns twentyApiKey
POST/auth/provision/demo-tenantDemo workspace + API key + Phone wiring
POST/auth/provision/sync-cockpit-action-mappingsPush Sales Cockpit mappings
GET/auth/provision/industry-preset-catalogPreset list for UIs
DELETE/auth/provision/workspace?subdomain=Hard-delete workspace (demo teardown)

SSO sequence​

All provisioning calls must run on the backend. The browser receives loginToken, expiresAt, workspaceOrigin, and workspaceUrl — never the provisioning secret.

Resolve​

POST /auth/provision/resolve
{
"email": "agent@example.com",
"firstName": "Alex",
"lastName": "Agent",
"tenantSubdomain": "acme",
"tenantDisplayName": "Acme Inc",
"avatar": "https://cdn.example.com/avatars/user.png",
"customSettings": {
"engagementInitiatorPhone": "6473710396"
}
}

tenantSubdomain optional — omit for personal workspace mode.

Optional avatar — same rules as login-token.

Optional customSettings — per-user CRM settings bag; see Custom settings (customSettings).

Response includes wasCreated: { workspace, user }. When wasCreated.workspace is true, the response also includes twentyApiKey — a long-lived Admin-role workspace API key (shown once). Store it server-side and use it for later /rest/* and /mcp calls on https://{subdomain}.{crm-host}. Repeat resolve for an existing workspace omits twentyApiKey.

{
"workspaceId": "...",
"subdomain": "acme",
"userId": "...",
"wasCreated": { "workspace": true, "user": true },
"twentyApiKey": "<jwt>"
}

Never return twentyApiKey to the browser. If the key was not stored, call POST /auth/provision/rotate-api-key (same PROVISIONING_SECRET as resolve). That invalidates existing Partner Provisioning keys and returns a new twentyApiKey (shown once). Do not use link-workspace or demo-tenant for this — those mint Hub/demo keys with different names.

Rotate partner API key​

POST /auth/provision/rotate-api-key
{
"workspaceId": "<uuid>",
"workspaceSubdomain": "acme"
}

One of workspaceId or workspaceSubdomain is required. 404 if the workspace is missing; 400 if neither identifier is sent.

{
"workspaceId": "<uuid>",
"subdomain": "acme",
"twentyApiKey": "<jwt>",
"revokedKeyCount": 1
}

revokedKeyCount is 0 when the workspace never received a partner key (still mints a new one). Keys created in CRM Settings → API keys, plus Engagement Hub CRM Sync and Demo Phone Sync, are left intact. Store twentyApiKey server-side; never send it to the browser.

Login token​

POST /auth/provision/login-token
{
"workspaceId": "...",
"email": "agent@example.com",
"firstName": "Alex",
"lastName": "Agent",
"page": "/objects/people",
"avatar": "https://cdn.example.com/avatars/user.png",
"customSettings": {
"engagementInitiatorPhone": "+15551234567"
}
}

page is optional — see Embedded page mode.

avatar is optional — a publicly accessible http or https image URL. CRM downloads the image, stores it as the workspace member profile picture, and uses it in the user profile and anywhere the member avatar is shown. Invalid URLs return HTTP 400. If the URL is unreachable or not an image, provisioning continues without an avatar (logged server-side).

customSettings is optional — see Custom settings (customSettings). The top-level engagementInitiatorPhone field is still accepted on this endpoint for backward compatibility; when both are sent, the top-level value wins.

Creates user + membership if missing. Redirect the browser to workspaceUrl once (first open, or when the CRM session is gone).

The login token is identity (email + workspace), not a page ticket. The response includes workspaceOrigin so hosts can reuse the same loginToken until expiresAt by changing returnToPath or sending postMessage { type: 'aventora.crm.navigate', page }. After the first /verify, CRM session cookies keep the user in.

The returned workspaceUrl always includes aventoraSso=1 (CRM hides Log out for SSO sessions). When page is set, the URL also includes returnToPath. Pass optional embedded: true to add aventoraEmbedded=1 (hides left nav — use for iframe embeds only, not new-tab deep links).

Custom settings (customSettings)​

Optional JSON object on user provisioning endpoints: resolve, login-token, user, and move-user. Integrators (Laravel, custom HTTP clients) can send per-user CRM values before CRM adds first-class API fields. Unknown keys are ignored (debug-logged server-side).

KeyTypeCRM behavior
engagementInitiatorPhonestringSets/overwrites userWorkspace.aventoraEngagementInitiatorPhone (North American 10-digit normalization). Omitted or empty leaves the stored value unchanged. Users can still edit Settings → Profile → Engagement callback number; a later partner provision with this key overwrites that value.

Example:

{
"workspaceId": "...",
"email": "agent@example.com",
"customSettings": {
"engagementInitiatorPhone": "6473710396"
}
}

To sync a number change from the partner platform, POST /auth/provision/user with workspaceId, email, and customSettings.engagementInitiatorPhone. Use PROVISIONING_SECRET. Do not use the workspace API key.

If a partner sends customSettings.engagementInitiatorPhone on every SSO (openCrm / login-token), the partner number wins over a number the user typed in CRM Profile.

On login-token, engagementInitiatorPhone may still be sent as a top-level field (legacy). Prefer customSettings for new integrations. Same overwrite rules apply.

Person ownership context (Hub contact creates)​

Engagement Hub attributes CRM people to the initiating user by calling this endpoint before POST /rest/people.

POST /auth/provision/person-ownership-context
{
"workspaceId": "...",
"email": "agent@example.com",
"engagementInitiatorPhone": "+15551234567"
}

At least one of email or engagementInitiatorPhone is required. Phone matches userWorkspace.aventoraEngagementInitiatorPhone (same normalization as login-token provisioning). If the user is not yet a workspace member, CRM provisions them with the Aventora User role (same as extra SSO users; the workspace owner is Admin).

Response:

{
"workspaceMemberId": "...",
"displayName": "Jane Agent",
"personWallScopeFieldName": "tipsAgentId",
"personWallScopeValue": 42
}

personWallScopeFieldName / personWallScopeValue are included when Person Wall is enabled and PERSON_WALL_FIELD_NAME includes a legacy numeric field (e.g. TIPS tipsAgentId or tipsAgentId,createdBy). Hub copies both createdBy.workspaceMemberId and the scope field onto new people.

Engagement Hub env (same values as domain-chatbot provisioning):

VariableDescription
CRM_API_URLApex URL for /auth/provision/person-ownership-context
CRM_PROVISIONING_SECRETMust match CRM PROVISIONING_SECRET

Domains must have crm_workspace_id in domain account_settings (set when CRM is enabled in aventora-admin).

Bulk SMS / calls: Person ownership is optional for CRM sync. Engagements, transcripts, and contact create/update still run via the workspace API key even when ownership lookup returns 404. Bulk rows often only carry user_phone_number (default broker phone) without a domain-chatbot user_id. Hub falls back to the Engagement Hub account email for ownership resolution when initiator email cannot be resolved from domain users. Phone-only lookup still requires userWorkspace.aventoraEngagementInitiatorPhone in CRM (set during SSO / login-token provisioning with engagementInitiatorPhone).

Embedded page mode (host-driven navigation)​

Use this when your app (not CRM) owns navigation — e.g. a sidebar or submenu in aventora-admin that opens CRM in an iframe or new window.

Partner iframe embeds require CRM env FRAME_ANCESTORS (CSP allowlist). Staging leaves it unset so framing is denied. See Laravel Integration — Embedded navigation.

ModeParametersCRM behavior
Full CRMNo pageNormal CRM with left navigation and mobile bottom nav
Embedded pagepage (+ embedded: true on direct API)Left nav and mobile bottom nav hidden; user lands on the requested screen

Laravel GET /crm/sso?page=... always sends embedded: true unless you pass embedded=0. Direct POST /auth/provision/login-token must include "embedded": true with page to hide nav (page alone only deep-links — used for Hub Call Log new-tab links that keep navigation).

In embedded mode:

  • Log out is hidden in CRM (user exits via your app)
  • Host menu owns section switches — mint login-token once, then postMessage navigate or rewrite returnToPath on the same token until expiresAt
  • The user may still follow in-page links (e.g. open a record); CRM nav stays hidden for that session
  • Do not call login-token on every submenu click

How to call SSO with page​

Laravel host app (authenticated):

GET /crm/sso?page=/objects/people
Query paramRequiredDescription
tenantNoCRM workspace subdomain. Omit for the user’s personal workspace.
pageNoFull internal CRM path (see Available pages). Omit for full CRM.
avatarNoPublic http/https image URL for the user’s CRM profile picture. Omit to leave unchanged.

Direct provisioning API (server-to-server):

POST /auth/provision/login-token
{
"workspaceId": "uuid",
"email": "user@example.com",
"page": "/objects/people",
"embedded": true,
"avatar": "https://cdn.example.com/avatars/user.png"
}

The same optional avatar field is supported on POST /auth/provision/resolve and POST /auth/provision/user.

SSO response (Laravel GET /crm/sso)​

{
"url": "https://{subdomain}.crm.example.com/verify?loginToken=...&aventoraSso=1&returnToPath=%2Fobjects%2Fpeople&aventoraEmbedded=1",
"loginToken": "...",
"expiresAt": "2026-...",
"workspaceOrigin": "https://{subdomain}.crm.example.com",
"workspaceId": "...",
"subdomain": "...",
"userId": "...",
"page": "/objects/people",
"embedded": true
}
FieldDescription
urlOpen this URL on first load (or when the iframe has no CRM session)
loginTokenReusable until expiresAt (also inside url)
expiresAtToken expiry (ISO timestamp). Mint again only after this.
workspaceOriginCRM origin for postMessage and for rewriting returnToPath
workspaceIdCRM workspace ID
subdomainWorkspace subdomain
userIdCRM user ID
pageEcho of requested page (only when provided)
embeddedWhether this session hides CRM chrome

The official plugin caches the login token per user + workspace. A second GET /crm/sso with a different page before expiry does not call CRM login-token again; it rewrites returnToPath. Your frontend should cache the same way and prefer postMessage when the iframe is already logged in. This JSON does not include twentyApiKey. On first workspace create the plugin fires CrmWorkspaceProvisioned so the Laravel backend can persist the key. After a rotate, listen for CrmWorkspaceApiKeyRotated the same way.

page parameter rules​

  1. Format: full internal CRM path, starting with /
    • Valid: /cockpit, /objects/people, /settings/profile
    • Invalid: cockpit, https://..., //evil.com
  2. Validation: invalid paths return HTTP 400 from login-token
  3. URL encoding: when passing page as a query param, encode it: page=%2Fobjects%2Fpeople
  4. Permissions: a valid path may still show an empty or restricted view if the user lacks CRM permissions
  5. Custom objects: use /objects/{pluralName} where {pluralName} is the object’s API plural name in that workspace

Blocked paths (return 400)​

Do not use auth, onboarding, or sign-up routes:

  • /welcome, /verify, /verify-email
  • /create/*, /invite-team, /plan-required, /book-call*
  • /reset-password/*
  • / (root alone)

Available page values​

Main app screens

Screenpage valueNotes
Sales Cockpit/cockpitDefault home when Sales Cockpit is enabled
People (list)/objects/peopleStandard CRM object
Companies (list)/objects/companiesStandard CRM object
Opportunities (list)/objects/opportunitiesStandard CRM object
Tasks (list)/objects/tasksStandard CRM object
Notes (list)/objects/notesStandard CRM object
Dashboards (list)/objects/dashboardsIf enabled in workspace
Workflows (list)/objects/workflowsIf enabled in workspace

Record detail pages (optional deep links)

Pattern: /object/{singularName}/{recordId}

Screenpage value
Person record/object/person/{uuid}
Company record/object/company/{uuid}
Opportunity record/object/opportunity/{uuid}

Replace {uuid} with the CRM record ID.

Settings screens (prefix /settings/)

In embedded mode (page + embedded: true / aventoraEmbedded=1), CRM hides all navigation including the settings sidebar. There is no /settings hub page — link each screen from your host app with its own page path (e.g. Profile, Accounts, Experience as separate menu items). See Laravel integration — Settings in embedded mode.

Settings areapage value
Profile/settings/profile
Experience (theme/locale)/settings/experience
Connected accounts/settings/accounts
Account emails/settings/accounts/emails
Account calendars/settings/accounts/calendars
Workspace general/settings/general
Data model/settings/objects
Members/settings/members
Roles/settings/roles
Domains/settings/domains
Billing/settings/billing
APIs & Webhooks/settings/api-webhooks
Apps/settings/applications
AI/settings/ai
Security/settings/security
Admin panel/settings/admin-panel
Updates/settings/updates

Settings sub-pages with IDs (roles, API keys, etc.) follow the same pattern, e.g. /settings/roles/{roleId}.

Custom workspace objects

/objects/{objectPluralName}
/object/{objectSingularName}/{recordId}

Example: custom object plural deals → /objects/deals

Call SSO once when the user opens CRM. Cache { loginToken, expiresAt, workspaceOrigin } until expiresAt.

Your app menuFirst open / token missAlready logged in
CRM Home (full)GET /crm/ssoin-session nav
Sales Cockpitpage=/cockpitpostMessage { type: 'aventora.crm.navigate', page: '/cockpit' }
Peoplepage=/objects/peoplepostMessage to /objects/people
Companiespage=/objects/companiespostMessage to /objects/companies
Opportunitiespage=/objects/opportunitiespostMessage to /objects/opportunities
Profilepage=/settings/profilepostMessage to /settings/profile
Experiencepage=/settings/experiencepostMessage to /settings/experience
Connected accountspage=/settings/accountspostMessage to /settings/accounts

If the iframe is not loaded, set iframe.src to {workspaceOrigin}{page} (session cookies). If that 401s or redirects to sign-in, open /verify with the cached loginToken and a new returnToPath. Mint a new login-token only after expiresAt.

Target origin for postMessage is workspaceOrigin. CRM ignores messages from origins not in FRAME_ANCESTORS, invalid page paths, and unknown types.

Embedded vs full CRM​

FeatureWithout pageWith page + embedded
Left navigationVisibleHidden
Mobile bottom navVisibleHidden
Log out in CRMHidden (SSO session)Hidden (SSO session)
Landing screenCRM default homeYour page path
Who navigatesUser (CRM nav)Your app menu (postMessage / returnToPath)

Verify embedded SSO​

  1. GET /crm/sso → full CRM with navigation
  2. GET /crm/sso?page=/cockpit → Cockpit only, no nav
  3. GET /crm/sso?page=/objects/people → People list, no nav
  4. GET /crm/sso?page=/welcome → 400 (invalid path)
  5. Switch submenu → no new login-token while the token is valid → correct screen each time
  6. Repeat /verify with the same token and a new returnToPath while already logged in → route change, not logout/login

Existing workspace shortcut​

When crm_workspace_id is already stored (aventora-admin after CRM enable), skip resolve and call login-token only with that workspaceId.

Add user explicitly​

POST /auth/provision/user with workspaceId + email. For SSO, login-token alone is usually enough.

Provision workspace with preset​

POST /auth/provision/workspace
{
"adminEmail": "admin@example.com",
"subdomain": "acme",
"displayName": "Acme Insurance",
"industryPreset": "insurance",
"industryProfile": "general_insurance_advisor"
}

Idempotent: if subdomain exists, adds/finds admin user and returns existing workspace.


Workspaces, users, and industry presets​

When is a workspace created?​

FlowWho picks workspaceCreates workspace?
resolve + tenantSubdomainClient passes subdomainYes, if subdomain new
resolve without tenantSubdomainCRM per emailYes, personal workspace if none exists
login-token with workspaceIdCaller (stored ID)Never
provision/workspace or demo-tenantExplicit API callYes, if subdomain new
TIPS tips:syncTIPS_SYNC_WORKSPACE_ID envNever
aventora-admin SSOcrm_workspace_id in domain settingsNever at login time

Resolve decision tree​

User already has a workspace membership:

  • Returns that workspace (no create)
  • If requested tenantSubdomain differs → 400

New user + tenantSubdomain provided:

  • Subdomain exists → join workspace, create user if needed
  • Subdomain missing → create shared workspace + user as admin

New user + no tenantSubdomain:

  • Personal workspace for email exists → reuse
  • Else → create personal workspace + user

Hard rules:

  • One workspace per user through provisioning (multi-membership → 400)
  • Cannot move user to another workspace via SSO (400) — use move-user instead
  • User exists with no membership → 400 (orphan; manual cleanup)

Workspace owner vs extra users​

On workspace create (resolve, provision/workspace, TIPS agent workspace), CRM assigns the standard Admin role to both AVENTORA_PLATFORM_ADMIN_EMAIL and the partner owner (adminEmail / TIPS agent). Extra users added later stay Aventora User (no Settings All Access). Existing tenants: workspace:backfill-provisioned-workspace-admins.

Admins must enable and use 2FA at login unless the CRM server sets USE_2FA=false.

Move user between workspaces​

POST /auth/provision/move-user moves a user's login membership from workspace A to workspace B. It does not migrate CRM records (People, engagements, cockpit history, etc.) — those stay in the source workspace.

Requirements:

  • User must belong to exactly one workspace (the from workspace)
  • Source workspace must have at least one other member (cannot move the only member out)
  • User cannot be the only admin in the source workspace unless another admin is assigned first
  • Target workspace must be active
POST /auth/provision/move-user
{
"email": "agent@example.com",
"fromWorkspaceSubdomain": "tenant-a",
"toWorkspaceSubdomain": "tenant-b",
"firstName": "Alex",
"lastName": "Agent",
"avatar": "https://cdn.example.com/avatars/agent.png"
}

Use fromWorkspaceId / toWorkspaceId instead of subdomains when IDs are already stored.

Response:

{
"userId": "uuid",
"email": "agent@example.com",
"fromWorkspaceId": "uuid",
"fromSubdomain": "tenant-a",
"toWorkspaceId": "uuid",
"toSubdomain": "tenant-b",
"userRestored": false
}

userRestored is true when the core user row was soft-deleted and restored during the move.

After a successful move, update external systems (crm_workspace_id, SSO login-token workspaceId) to the to workspace. Issue a new login-token for the target workspace on next SSO.

SituationResult
User not found404
User not in from workspace400
User already in to workspace400 (use login-token only)
User in multiple workspaces400
Only member of from workspace400
Only admin of from workspace400
from and to are the same400
Target workspace inactive400

Industry presets​

Presets apply once at workspace activation when industryPreset is set on the workspace row. They add labels, custom fields, views, pipelines, dashboards, CRM workflow templates (DRAFT), Sales Cockpit automation rules, and Engage by Aventora defaults (AVENTORA_DEFAULT_CHANNEL, AVENTORA_DEFAULT_TYPE, AVENTORA_DEFAULT_INSTRUCTION). Apply/reapply overwrites those three workspace application variables so Custom Start Aventora matches the preset. Admins can still edit them afterward in Settings → Apps.

When a preset renames Person, Company, or Opportunity, the CRM also renames reciprocal relation field labels on linked records (for example, after Company becomes Employer, the Person detail field company shows Employer instead of Company). Only objects explicitly defined in the preset are updated; labels are not reset when a preset omits them.

Operators can export a built-in preset as JSON (workspace:export-industry-preset) for a partner to edit, then apply that file with workspace:apply-industry-preset --from-file. JSON is nested (field groups, pipeline stages, workflow steps, Engage defaults); CSV is a fields review sheet only. Partner files are ops CLI only — they do not appear in the signup catalog and are not accepted as industryPreset on POST /auth/provision/*. New workspaces still auto-apply catalog presets at activation.

Aventora workflow templates (v1)​

Industry presets seed inactive Aventora automation in two layers. Templates are not created by upgrade alone on workspaces that already have presetAppliedAt set — run workspace:reapply-industry-preset after deploying a build that includes workflow seeding.

LayerWhere in CRMHow to findDefault stateUser activates
Aventora CRM WorkflowsWorkflows moduleSettings → Workflows — names like Bulk SMS: Reconnect (internal [preset:…] key is hidden)DRAFTOpen template → review → Activate
Sales Cockpit rulesCore aventoraAutomationRule tableSales Cockpit (/cockpit) → Automation Suggestions panelDRAFT / disabledEnable rule; optional Auto-run when triggered

Manual CRM workflows also appear on People records (single-record or bulk selection) via the workflow/command menu after activation.

Step-by-step descriptions for workspace users — who is messaged, when the workflow runs, what the message is, and what to check before Activate — are in the CRM User Guide — Workflow templates. The tables below are the operator index (internal key, trigger, and a one-line summary).

CRM workflow templates (all industries)​

KeyUI nameTriggerWhat it does
av_wf_manual_engage_personEngage Person (Aventora)Manual, single personAsks for channel and instruction, then starts a follow-up on that person. See Engage Person.
av_wf_new_person_say_helloNew Person: Say Hello (SMS)person.createdHello SMS for every new person. See Say Hello.
av_wf_new_person_qualify_callNew Person: Qualify (Call)person.createdQualification call for every new person. See Qualify (Call).
av_wf_bulk_sms_checkinBulk SMS: Check InterestManual bulk peopleInterest-check SMS for the people you select. See Check Interest.
av_wf_bulk_reconnectBulk SMS: ReconnectManual bulk peopleReconnect SMS for the people you select. See Reconnect.
av_wf_bulk_booking_linkBulk SMS: Send Booking LinkManual bulk peopleBooking-link SMS for the people you select. See Send Booking Link.
av_wf_happy_birthdayHappy BirthdayDaily CRON 0 14 * * * (14:00 UTC)SMS when Date of Birth matches today’s month and day. See Happy Birthday.
av_wf_holiday_new_yearHappy Holiday: New YearYearly CRON 0 14 1 1 *January 1 SMS, up to 200 people. See New Year.
av_wf_holiday_canada_dayHappy Holiday: Canada DayYearly CRON 0 14 1 7 *July 1 SMS, up to 200 people. See Canada Day.
av_wf_holiday_independence_dayHappy Holiday: Independence DayYearly CRON 0 14 4 7 *July 4 SMS, up to 200 people. See Independence Day.
av_wf_holiday_christmasHappy Holiday: ChristmasYearly CRON 0 14 25 12 *December 25 SMS, up to 200 people. See Christmas.
av_wf_manual_thank_youThank You After MeetingManual, single personThank-you SMS you start from one person. See Thank You After Meeting.

Holiday templates are separate workflows. Activate only the ones that apply (Canada Day vs Independence Day). Shared industries do not seed Thanksgiving, Victoria Day, Family Day, or Easter because those dates move. Reable Real Estate seeds those four as manual DRAFT blasts (run from Workflows on the holiday) instead of crons.

Date of Birth: Standard Person has no birthdate. Insurance uses insDateOfBirth. Generic, real estate, mortgage, and financial advisor seed Date Of Birth (avDateOfBirth). Birthday lookup uses the first of those fields that exists. People without a date of birth are skipped.

Cron time is UTC. Admins can edit the schedule after seed. Holiday/birthday blasts stay DRAFT until activated because they can message many people. Query audiences skip do not contact and missing phone. If nobody matches, the step skips instead of failing the run.

Industry CRM workflow templates​

IndustryKeyUI nameTriggerWhat it does
Insuranceav_wf_ins_renewal_reminderPolicy Renewal ReminderDaily CRON 14:00 UTCTexts the client when insRenewalDate is exactly 30, 15, 7, 3, or 0 days away (UTC). Admins change that list in Days before on the query step. See Policy Renewal Reminder.
Insuranceav_wf_ins_lapsed_policyLapsed Policy Outreachpolicy.updated (insPolicyStatus) + FILTER LAPSEDTexts the client only when status becomes Lapsed. See Lapsed Policy Outreach.
Insuranceav_wf_ins_quote_presentedQuote Presented Follow-Upopportunity.updated (insPolicyPipelineStage) + FILTER Quote PresentedTexts the quote contact only when the stage becomes Quote Presented. See Quote Presented Follow-Up.
Real estateav_wf_re_follow_up_dueFollow-Up Due TodayDaily CRON 14:00 UTCTexts people whose reNextFollowUpDate is today. See Follow-Up Due Today.
Real estateav_wf_re_new_listingNew Listing Check-Inlisting.createdTexts the contact linked on a new listing. See New Listing Check-In.
Real estateav_wf_re_showing_scheduledShowing Scheduled Follow-Upopportunity.updated (reBuyerPipelineStage) + FILTER Showing ScheduledTexts the deal contact only when the buyer stage becomes Showing Scheduled. See Showing Scheduled Follow-Up.
Mortgageav_wf_mtg_closing_reminderClosing ReminderDaily CRON 14:00 UTCTexts the borrower when mtgClosingDate is exactly 30, 15, 7, 3, or 0 days away (UTC). Admins change that list in Days before on the query step. See Closing Reminder.
Mortgageav_wf_mtg_funded_thank_youFunded Thank Youopportunity.updated (mtgMortgagePipelineStage) + FILTER FundedThank-you SMS only when the stage becomes Funded. See Funded Thank You.
Financial advisorav_wf_fa_review_dueReview Due TodayDaily CRON 14:00 UTCBooking-link SMS when faNextReviewDate is today. See Review Due Today.
Financial advisorav_wf_fa_ask_referralAsk For ReferralManual, single personSMS you start from one person, asking for an introduction. See Ask For Referral.
Reable Real Estateav_wf_reable_portal_leadPortal Lead: Website Form (SMS)person.created + FILTER WEBSITE_FORMExact SMS when a person is created with Lead Source Website form. See Portal Lead.
Reable Real Estateav_wf_reable_seller_valuationSeller Valuation Request (SMS)person.created + FILTER HOME_VALUATIONExact SMS when a person is created with Lead Source Home valuation. See Seller Valuation.
Reable Real Estateav_wf_reable_holiday_victoria_dayHappy Holiday: Victoria DayManual (global)You run it on the holiday. Exact SMS, up to 200 people. See floating holidays.
Reable Real Estateav_wf_reable_holiday_family_dayHappy Holiday: Family DayManual (global)You run it on the holiday. Exact SMS, up to 200 people. See floating holidays.
Reable Real Estateav_wf_reable_holiday_thanksgivingHappy Holiday: ThanksgivingManual (global)You run it on the holiday. Exact SMS, up to 200 people. See floating holidays.
Reable Real Estateav_wf_reable_holiday_easterHappy Holiday: EasterManual (global)You run it on the holiday. Exact SMS, up to 200 people. See floating holidays.

Generic industry gets the shared templates only. Reable also inherits the stock real-estate templates.

Workflow steps use custom action types START_AVENTORA_ENGAGEMENT (single person), START_AVENTORA_ENGAGEMENT_BULK (known people), and START_AVENTORA_ENGAGEMENT_QUERY (scheduled audience lookup). All call the shared AventoraEngagementExecutionService, which respects doNotContact on the person and uses workspace Aventora app variables + cockpit action mappings.

The workflow editor Cockpit action type control is a dropdown of executable cockpit actions (SEND_FOLLOW_UP, CALL_LEAD, …). That mapping selects Hub type (informational, conversational, appointment booking, …). Instruction is a multiline field prefilled from the selected action; you can edit it before the workflow runs.

Cockpit automation templates​

KeyNameSignalsDefault action
av_cockpit_missed_callMissed Call Follow-UpMISSED_CALLCALL_LEAD
av_cockpit_no_replyNo Reply Follow-UpNO_REPLYSEND_FOLLOW_UP

When Auto-run is enabled on an active rule, the cockpit execution path applies cooldown and max executions per lead per day guards before calling Hub.

GraphQL (cockpit rules): updateAventoraAutomationRule — toggle enabled and autoExecute from the Automation Suggestions UI.

Compliance​

Auto workflows (person.created, daily/yearly CRON birthday and holiday blasts, cockpit auto-run) can contact leads without a manual click. Review every template before activation. People marked do not contact or missing the channel contact field (phone for SMS/call, email for email) are skipped. Date-based audiences also require that date. Holiday query audiences are capped at 200.

The full catalog is served at runtime by GET /auth/provision/industry-preset-catalog. Current values:

industryPresetindustryProfile valuesDefault profileProfile required?
real_estatebuyer_agent, listing_agent, team_leader, broker, general_realtorgeneral_realtorYes
insurancepersonal_lines, commercial_lines, benefits_advisor, broker_owner, general_insurance_advisorgeneral_insurance_advisorYes
mortgagemortgage_agent, mortgage_broker, commercial_mortgage, team_lead, broker_ownermortgage_agentYes
financial_advisorfinancial_advisor, wealth_advisor, retirement_specialist, team_lead, practice_ownerfinancial_advisorYes
generic (aliases: general, other)defaultdefaultNo
reable_real_estate (alias: reable-real-estate)defaultdefaultNo

Profile rules:

  • For real_estate, insurance, mortgage, and financial_advisor, industryProfile is required — a missing or unknown profile returns 400.
  • For generic and reable_real_estate, industryProfile is optional and falls back to default.
  • Unknown industryPreset values return 400. The legacy onboarding ids general and other resolve to generic.

Important: industryPreset / industryProfile are applied only when a workspace is created. When a call reuses or joins an existing workspace, the preset is ignored (the workspace keeps whatever preset it was created with). This applies to every endpoint that accepts the fields — resolve, workspace, and demo-tenant.

POST /auth/provision/resolve does accept industryPreset / industryProfile, but only takes effect when resolve creates a new workspace (new tenantSubdomain, or a new personal workspace when no subdomain is passed). A resolve that joins an existing subdomain — or returns a user's existing membership — gets no preset (generic CRM + standard Aventora fields). See Laravel Integration — resolve.

To control preset:

  1. Pass industryPreset + industryProfile on the call that creates the workspace — POST /auth/provision/resolve (new subdomain/personal), POST /auth/provision/workspace, or demo-tenant
  2. Use onboarding UI (self-serve signup)
  3. Run workspace:apply-industry-preset after the fact (first time or to change preset/profile — required for existing workspaces, since preset is not re-applied on join)
  4. Run workspace:reapply-industry-preset after upgrades to backfill new template artifacts on workspaces that already have a preset (also creates Listing / Policy custom objects when those industries are upgraded to preset v1.1.0+, Insurance client fields / marital-status options when upgraded to preset v1.2.0+, Date of Birth plus birthday/holiday/industry workflow templates on later versions, Reable Real Estate v1.1.0 portal/valuation and floating-holiday DRAFT workflows plus Home valuation lead source, and Engage defaults for channel / type / instruction)
  5. For a partner-authored JSON file (not a catalog id), export with workspace:export-industry-preset and apply with workspace:apply-industry-preset --from-file. Custom ids cannot be re-applied with workspace:reapply-industry-preset.

TIPS: One shared workspace is created with insurance preset before sync. tips:sync only creates users in that workspace — not new workspaces or presets per agent.

Color theme presets​

Workspace admins can choose an accent color theme for the whole CRM UI. This is separate from industry presets (which seed CRM data and workflows). Color themes only change accent colors — buttons, links, highlights, and related UI chrome. They do not switch light vs dark mode.

CRM has two independent appearance controls:

SettingWhat it changesScopeWhere in CRM
Color theme (themePreset)Accent palette (Classic, Ocean, Forest, …)Entire workspace — all members see the same accentsSettings → Workspace → General → Color theme
Light / Dark / System (colorScheme)Light mode, dark mode, or follow OSPer user — each member chooses their ownTop workspace menu → Theme, or Settings → User → Experience → Appearance

The workspace dropdown Theme submenu (System / Dark / Light) is the same personal colorScheme setting as User → Experience — not the workspace color theme.

Workspace color theme changes apply immediately for all members after save. Default for new and existing workspaces (post-migration) is classic (classic indigo).

Available presets​

themePreset valueLabelAccent palette
classicClassicIndigo (classic default)
oceanOceanTeal
forestForestJade
sunsetSunsetOrange
violetVioletViolet
roseRoseCrimson
midnightMidnightIris

Valid values are enforced on updateWorkspace; invalid IDs are rejected.

API and storage​

  • Column: core.workspace."themePreset" (default 'classic')
  • Migration: applied automatically during normal CRM database upgrade
  • GraphQL: updateWorkspace(data: { themePreset: "ocean" }) on workspace metadata API
  • Permission: PermissionFlagType.WORKSPACE (same as workspace name/logo)

Check current value:

SELECT id, "displayName", "themePreset"
FROM core.workspace
WHERE id = '<uuid>';

Programmatic update (admin session or API key with workspace settings access):

mutation UpdateWorkspaceTheme {
updateWorkspace(data: { themePreset: "ocean" }) {
id
themePreset
}
}

Deploy note​

After upgrading to a build that includes color themes:

  1. Ensure core migrations have run (yarn command:prod upgrade) — adds core.workspace.themePreset.
  2. No extra CLI command is required; admins pick a preset in Settings → Workspace → General.

Color themes and industry preset workflows ship in the same CRM build but are configured independently.


Sales Cockpit​

Signal-driven sales layer on Person records: ingest signals → scores → suggested actions → /cockpit dashboard and Person Sales tab.

Enablement checklist​

  1. Deploy with upgrade (Sales Cockpit migrations)
  2. workspace:sync-aventora-sales-person-fields and workspace:sync-aventora-variables
  3. Wire Hub↔CRM: AVENTORA_API_KEY, webhooks, AVENTORA_WEBHOOK_SECRET
  4. aventora-admin: Enable Aventora CRM; configure cockpit action mappings
  5. Confirm IS_SALES_COCKPIT_ENABLED on workspace
  6. Optional demo data: workspace:seed-aventora-sales-cockpit-demo

Feature flags​

FlagEffect
IS_SALES_COCKPIT_ENABLED/cockpit nav, default home route
IS_SUGGESTED_ACTIONS_ENABLEDReserved

Dashboard panels​

Priority Leads, Suggested Actions, Reconnect Recent, Reconnect Oldies, Stale Leads, Hot Leads, Recently Engaged, Leads Requiring Follow-up, Team Activity, Automation Suggestions (enable preset rules and optional Auto-run when triggered; respects cooldown and daily max per lead).

Signal sources​

SourceHow
Hub webhooksEngagement outcomes → signals
RESTPOST /rest/sales-intelligence/signals
GraphQLcreateAventoraPersonSignal
Demoworkspace:seed-aventora-sales-cockpit-demo

Cockpit action → Hub mappings​

Configured in aventora-admin → Account Settings → CRM Sync when provider is Aventora CRM.

Stored in domain-chatbot twenty_cockpit_action_mappings, synced to CRM app var AVENTORA_COCKPIT_ACTION_MAPPINGS via POST /auth/provision/sync-cockpit-action-mappings.

Hub accepts informational, conversational, appointment_booking, confirmational — not raw cockpit action names like ESCALATE_TO_SALES.

Cockpit APIs​

  • GraphQL: aventoraSalesCockpitSummary, aventoraPersonIntelligence, …
  • REST: GET /rest/sales-intelligence/cockpit-summary, POST /rest/sales-intelligence/signals
  • Execute: POST /rest/aventora/start-engagement with cockpitActionType

Sales Cockpit troubleshooting​

SymptomLikely causeFix
"Sales cockpit is not available yet."GraphQL query failed (not empty data)Browser DevTools → Network → GetAventoraSalesCockpitSummary → read errors[0].message. Check server logs.
SQL error on load (e.g. missing column)Migrations or person fields not syncedupgrade, workspace:sync-aventora-sales-person-fields
Panels show "No leads to show"Query succeeded, no signalsworkspace:seed-aventora-sales-cockpit-demo or ingest signals
Execute 400, invalid Hub typeMappings not syncedAdmin → Sync to CRM
Feature missingFlag offIS_SALES_COCKPIT_ENABLED
No [preset:…] workflows after upgradeWorkspace already had presetAppliedAt; templates not backfilledworkspace:reapply-industry-preset
reapply-industry-preset errors on workflow insertOlder workspace workflow metadataUpgrade to latest build (applier omits legacy actor columns); re-run workspace:reapply-industry-preset
Workflows nav shows 404 ("Off the beaten path") while other objects workUser's assigned role lacks Workflows settings permission (WORKFLOWS flag). Workflow objects require that flag even when canReadAllObjectRecords is true. Common on tenant workspaces (isPersonalWorkspace: false) when the user is still on Member instead of Aventora User.Settings → Roles → Aventora User → enable Workflows, or reassign the user to Aventora User. On VPS: yarn command:prod workspace:ensure-aventora-user-role-permissions (also reassigns members on Hub-integrated workspaces), then cache:flush, log out and back in. Console may log [AventoraSSO] record-index-denied-read-access.

Debug GraphQL: failed cockpit load always means backend error. Empty data still shows panel grid with "No leads to show."


CRM-first Hub provisioning (existing CRM workspaces)​

Automatic (CRM-triggered)​

When AVENTORA_HUB_AUTO_PROVISION_ENABLED=true on aventora-crm, provisioning a workspace or adding a workspace member enqueues a BullMQ job that calls Aventora Assistant POST /internal/crm-hub-provision. CRM holds only the callback URL + shared secret; domain-chatbot super-admin credentials and telephony env stay on Assistant.

CRM envAssistant env
AVENTORA_HUB_AUTO_PROVISION_ENABLED=trueCRM_HUB_PROVISION_CALLBACK_SECRET (same value as CRM secret)
AVENTORA_HUB_PROVISION_CALLBACK_URLDOMAIN_CHATBOT_*, TWILIO_ACCOUNT_SID / TWILIO_AUTH_TOKEN / TWILIO_SMS_NUMBER (same as Hub runtime)
  • New workspace (via /auth/provision/* createWorkspaceWithAdmin): job action=full after ~60s delay (deduped per workspace).
  • New workspace member: job action=sync_members after ~30s delay (skipped server-side if Hub not linked yet).

Requires CRM worker process running (same as TIPS sync). Failures retry up to 3 times; CRM provisioning itself never fails because of Hub auto-provision.

Client Growth Hub uses this same POST /auth/provision/resolve path when an organization is created. The subdomain is the organization slug, except the reserved platform shell aventora, which is stored as aventora-cgh. Hub then creates the domain, account, and default Connect campaign. That campaign URL is the organization's Google ad landing page. Existing organizations are backfilled with the same call.

Partner workspace billing defaults (Hub): After domain + account creation, Hub applies pay-as-you-go + credit, 100 welcome credits (PARTNER_CRM_INITIAL_CREDITS or DEFAULT_INITIAL_CREDITS), and platform default engagement rates. Only runs while the account is still on the initial free plan from registration.

Manual batch (ops backfill)​

When CRM workspaces already exist but domain-chatbot, Engagement Hub, and CRM↔Hub wiring are missing, your Aventora operator runs a provisioning backfill from the Hub deployment. That process:

  1. Lists CRM workspaces that are not yet linked to Hub.
  2. Creates the matching domain-chatbot tenant and Hub account for each workspace.
  3. Applies shared inbound/Twilio defaults and stores CRM link credentials.
  4. Optionally syncs workspace members into domain-chatbot and Hub when members were added before linking.

Contact your Aventora administrator or support if you need this backfill for an existing deployment.

Required env (Hub / aventora-phone)​

VariableUsed by
DOMAIN_CHATBOT_API_URL (or DOMAIN_CHATBOT_URL)Workspace list + reverse provision API
DOMAIN_CHATBOT_SUPER_ADMIN_TOKEN or SUPER_ADMIN_USERNAME + SUPER_ADMIN_PASSWORDdomain-chatbot super-admin auth
CRM_API_URL, CRM_PROVISIONING_SECRETPerson ownership context for Hub-created CRM contacts
DATABASE_URL (optional)Hub accounts.domain_name lookup in dry-run summary
TWILIO_ACCOUNT_SID, TWILIO_AUTH_TOKEN (or TWILIO_API_KEY_SID + TWILIO_API_SECRET), TWILIO_SMS_NUMBER / TWILIO_PHONE_NUMBERTelephony stamped onto new Hub/domain accounts (same vars as Hub runtime). Optional PROVISION_* overrides still accepted
TWILIO_MASTER_ACCOUNT_SID, TWILIO_MASTER_AUTH_TOKEN (or TWILIO_MASTER_API_KEY + TWILIO_MASTER_API_SECRET), TWILIO_WEBHOOK_BASE_URLaventora-admin Add Domain auto-provision (search/buy number, create API key)
TWILIO_PROVISION_TEST_MODE=true (domain-chatbot, test credentials only)E2E auto-provision dry run: magic number search results, synthetic API key, purchase +15005550006 via Twilio test API (no real number charges). Disable before production provisioning.
CRM_HUB_PROVISION_CALLBACK_SECRETAuthenticates CRM POST /internal/crm-hub-provision (same value as CRM AVENTORA_HUB_PROVISION_CALLBACK_SECRET)

On AWS staging, Hub receives DOMAIN_CHATBOT_SUPER_ADMIN_TOKEN from the same Secrets Manager value as Chatbot PLATFORM_ADMIN_API_KEY (platform-wide domain API key). Username/password remain valid for VPS/Compose and bootstrap superadmin ensure-super-admin (authoritative CHATBOT_SUPER_ADMIN_* in platform/shared). Chatbot CRM-provision routes accept either a super-admin JWT or that platform-wide API key via Authorization: Bearer.

CRM CRM_API_URL and CRM_PROVISIONING_SECRET are required on Engagement Hub for person ownership resolution (Hub calls POST /auth/provision/person-ownership-context when creating CRM contacts). They remain required on domain-chatbot for workspace list, link-workspace, and demo provisioning.

Required env (domain-chatbot)​

VariableUsed by
CRM_API_URL, CRM_PROVISIONING_SECRETCRM workspace list + link-workspace

Inbound phone and Twilio credentials for new domains can be set during aventora-admin → Add Domain (optional telephony section: manual API key + number, or auto-provision from the platform master Twilio account). Classic Auth Token is only available post-provision in Account Settings. CRM workspace backfill (CLI / provision-from-crm-workspace) still accepts operator-supplied env or payload credentials including classic auth token.

Prerequisites​

  • CRM deployed with migrations and workspace:sync-aventora-variables
  • domain-chatbot and Engagement Hub running with service keys configured
  • CRM workspace subdomain (or AVENTORA_ASSIGNED_DOMAIN) must be a valid domain slug: [a-z0-9-]{3,30}

Demo workspace provisioning​

Ephemeral demos provision a CRM workspace via domain-chatbot + POST /auth/provision/demo-tenant.

domain-chatbot env​

VariableDescription
CRM_API_URL, CRM_PROVISIONING_SECRETCRM provisioning
CRM_PUBLIC_BASE_URLWorkspace URL builder
CRM_WEBHOOK_SECRETMust match CRM AVENTORA_WEBHOOK_SECRET
DEMO_ENABLE_CRMDefault true; false to skip
HUB_API_URL, HUB_API_KEYDemo engagement

CRM endpoints​

  • POST /auth/provision/demo-tenant — workspace + twentyApiKey + Phone wiring (supports industryPreset)
  • DELETE /auth/provision/workspace?subdomain= — teardown

Verification​

  1. Create demo invitation in admin
  2. Confirm demo_config has crm_workspace_id, crm_subdomain
  3. Open demo live page → CRM tab
  4. Run engagement demo → contact appears in CRM
  5. On revoke/expiry → workspace deleted

Demo slugs capped at 30 characters for CRM subdomain validation.


TIPS Services sync​

Optional: sync TIPS Advisors agents → CRM users; clients → people. Ships on main; enable with TIPS_* env.

Setup (Tip Services)​

  1. Create workspace (onboarding: Insurance Brokerage preset, or workspace:apply-industry-preset)
  2. Set TIPS_SYNC_WORKSPACE_ID on server and worker
  3. tips:bootstrap-fields (fields + Member (Aventora Hub) role)
  4. tips:sync
  5. Optional schedule: TIPS_SYNC_ENABLED=true, recreate server or cron:tips:sync

Inbound sync​

For each TIPS agent with crmEnabled:

  • ensureUserInWorkspaceForTipsSync — same primitive as SSO findOrCreateUserInWorkspace (+ password sync, role from crmHubEnabled)
  • Updates workspaceMember tips* fields
  • Upserts Person records for clients

TIPS vs client SSO​

Client SSO (resolve)TIPS sync
WorkspacePer tenantSubdomain or personalFixed TIPS_SYNC_WORKSPACE_ID
Creates workspaceSometimesNever
Industry presetOnly via provision/workspaceOn pre-built workspace
TriggerUser clicks Open CRMCron / CLI

Verify scheduled sync​

docker compose ps worker
docker compose logs server 2>&1 | grep -i TipsSync
docker compose logs worker --since 24h | grep -i tips

Laravel plugin​

Building your own Laravel integration? Use Laravel Integration — the shareable guide for custom HTTP clients (provisioning, SSO, page, avatar). This section covers the official Composer package only.

Package: aventora-crm/laravel-plugin/

composer require aventora/crm-laravel
php artisan vendor:publish --tag=crm-config
AVENTORA_CRM_URL=https://crm.example.com
AVENTORA_CRM_PROVISIONING_SECRET=...
AVENTORA_CRM_DEFAULT_TENANT=acme # optional shared subdomain

Route: GET /crm/sso → resolve → login-token → open CRM.

Optional query param page — full CRM path for embedded page mode (deep-links; Laravel route also sends embedded to hide CRM nav):

GET /crm/sso?page=/objects/people
GET /crm/sso?tenant=acme&page=/cockpit

Response JSON includes url (ready to open), plus page when requested. See SSO response for field list. GET /crm/sso never returns twentyApiKey (long-lived Admin key). Listen for Aventora\Crm\Events\CrmWorkspaceProvisioned on first workspace create to persist it server-side. If that key was not stored, call Crm::rotateWorkspaceApiKey() and listen for CrmWorkspaceApiKeyRotated.

Health check:

php artisan crm:health-check --email=demo@local.test --tenant=acme

Blade:

@include('crm::components.button', ['tenant' => 'acme', 'label' => 'Open CRM'])

Contacts and workspace API keys​

Base URL: https://{subdomain}.{crm-host}

POST /rest/people
Authorization: Bearer <CRM_WORKSPACE_API_KEY>
Content-Type: application/json

{
"name": { "firstName": "Jordan", "lastName": "Client" },
"emails": { "primaryEmail": "jordan@example.com", "additionalEmails": [] },
"phones": {
"primaryPhoneNumber": "5551234567",
"primaryPhoneCountryCode": "US",
"primaryPhoneCallingCode": "+1",
"additionalPhones": []
}
}

API keys from first-time resolve (twentyApiKey, store once), POST /auth/provision/rotate-api-key, link-workspace, demo-tenant, or CRM Settings → API keys.

Bulk email / engagement (workspace API key)​

POST /rest/aventora/start-engagement-bulk sends informational email (or phone/SMS) through Engagement Hub.

  • Some contacts: { "personIds": ["…"], "channel": "email", "type": "informational", "emailSubject": "…", "emailBodyHtml": "<p>…</p>" } (max 1000 ids).
  • All contacts: { "audience": "all", "channel": "email", "type": "informational", "emailSubject": "…", "emailBodyHtml": "<p>…</p>" }. Do not send personIds or viewId. CRM skips do not contact and people without email, then chunks Hub submits at 1000.
  • Person view: GET /rest/aventora/person-views then { "viewId": "…", "channel": "email", "type": "informational", "emailSubject": "…", "emailBodyHtml": "<p>…</p>" }. Mutually exclusive with personIds and "audience": "all". CRM applies the view’s filters at send time.

Use a Hub template (emailTemplateId + emailTemplateParams) instead of emailBodyHtml when the layout is saved in Admin. htmlBody / html_body is accepted as the same inline HTML. Optional fromName / from_name sets the From display name; otherwise CRM uses Profile / default initiator CRM user name / workspace display name. See Outbound Email Templates.


MCP (Cursor / Claude)​

Workspace API keys can also call POST /mcp (JSON-RPC 2.0). That endpoint is the Aventora CRM MCP server: named tools wrap /rest/people and /rest/notes. Copy the Cursor snippet from Settings → AI. Full tool list: CRM MCP.

Claude Custom Connectors use the same /mcp URL with OAuth (authorization-code + PKCE), not an API key. Operators register the confidential client with application:register-claude-mcp-client. See CRM MCP.

For outbound calls and SMS, use the Engagement Hub MCP with a Hub API key — not CRM MCP.


Webhooks​

Hub → CRM: POST https://{subdomain}.{host}/rest/aventora/webhook with X-Aventora-Webhook-Secret.

CRM → Hub: per-workspace AVENTORA_API_KEY + server AVENTORA_BASE_URL.


Troubleshooting​

Provisioning / SSO​

# 1. Right host?
curl -sS "https://CRM_HOST/healthz"

# 2. Route exists? (expect 401, not 404)
curl -sS -X POST "https://CRM_HOST/auth/provision/resolve" \
-H "Authorization: Bearer test" \
-H "Content-Type: application/json" \
-d '{"email":"debug@test.example","tenantSubdomain":"test"}'

# 3. Valid secret (expect 200)
curl -sS -X POST "https://CRM_HOST/auth/provision/resolve" \
-H "Authorization: Bearer YOUR_SECRET" \
-H "Content-Type: application/json" \
-d '{"email":"debug@test.example","tenantSubdomain":"test"}'
HTTPMeaning
404 on /auth/provision/*Wrong host, old image, or proxy path
401Wrong PROVISIONING_SECRET
400 on resolveWorkspace conflict, multi-workspace user, orphan user
400 on move-userWrong membership state, only member/admin, inactive target workspace

Never expose provisioning secret or workspace API keys in browser JavaScript.

Error handling checklist​

  • 404 on provision routes → hostname/deploy, not secret
  • 401 → secret mismatch
  • 400 on resolve → tenant/user conflict
  • 404 on login-token → unknown workspaceId
  • 400 on login-token → inactive workspace, or invalid page path (embedded mode)
  • User does not have permission when changing color theme → role lacks Workspace settings permission, or server build missing themePreset in workspace field permissions; grant Workspace on the role or upgrade server

Changelog​

| Date | Change | | --- | | 2026-10-05 | Insurance preset v1.5.0: Clients gain insFolderName and insSearchKey. Policies gain insProduct, insPremiumAmount, insDeductibleAmount, insExpiryDate, insPremiumNumeric, and insCoverageNumeric. Notes stay the Notes object. Existing insurance and TIPS workspaces pick these up via workspace:reapply-industry-preset. | | 2026-10-05 | POST /auth/provision/reprovision-hub with connectUrlOnly: true reads the existing Connect campaign URL. It does not create another Hub account or domain. | | 2026-10-04 | Client Growth Hub calls POST /auth/provision/resolve when an organization is created. CRM then provisions the Hub account and domain. The default Connect campaign is that organization's Google ad landing page. Existing organizations are backfilled with the same call. | | 2026-09-29 | Renewal and closing reminders: av_wf_ins_renewal_reminder and av_wf_mtg_closing_reminder match exact day offsets (default 30, 15, 7, 3, 0) instead of a daily window. workspace:reapply-industry-preset updates a version only while its audience is still the original within-30 or within-7 search. A saved Days before list is left as-is. | | 2026-09-29 | Workflow template descriptions: User-facing write-ups for every seeded CRM workflow (shared, insurance, real estate, mortgage, financial advisor, Reable) are in the CRM User Guide. This guide keeps the operator key index. | | 2026-09-28 | Related-record spreadsheet import: Importing a record with a many-to-one person relation (Policy client, Listing contact, Opportunity point of contact, and the same link in every preset) matches the person by email, then phone. See Importing records. | | 2026-09-24 | Partner bulk email billing: POST /rest/aventora/start-engagement-bulk with channel: "email" records an outbound-email usage row in Engagement Hub when each row is accepted. | | 2026-09-21 | Partner bulk email From: Laravel htmlBody is accepted on POST /rest/aventora/start-engagement-bulk. From display name keeps the CRM user/Profile name (including API-key default initiator user account name), not Hub EMAIL_DISPLAY_NAME. | | 2026-09-19 | Partner bulk email From: API-key POST /rest/aventora/start-engagement-bulk falls back to workspace display name and optional fromName / from_name. | | 2026-09-17 | Partner preset JSON: workspace:export-industry-preset writes a catalog preset as JSON (optional --format csv fields sheet). workspace:apply-industry-preset --from-file applies a validated file to one workspace or, with --force --all-workspaces, every workspace. File presets are ops CLI only — not onboarding and not industryPreset on provision. Custom industry ids re-apply with --from-file, not workspace:reapply-industry-preset. | | 2026-09-17 | Industry preset Engage defaults: Applying or re-applying a preset writes AVENTORA_DEFAULT_CHANNEL, AVENTORA_DEFAULT_TYPE, and AVENTORA_DEFAULT_INSTRUCTION (Custom Start Aventora). Existing workspaces pick this up via workspace:reapply-industry-preset. This overwrites those three Settings → Apps values. | | 2026-09-17 | Compose healthcheck: Server /healthz has a 6-minute start_period so first-boot migrations do not mark the container unhealthy or leave the worker in Created. | | 2026-09-14 | GHCR production images: GitHub Actions builds linux/amd64 CRM images and publishes them to GitHub Container Registry. Prefer sha-* tags for exact deploys; latest is the latest successful main build. | | 2026-09-12 | Engagement callback overwrite: Partner customSettings.engagementInitiatorPhone on POST /auth/provision/user (and the shared helper on resolve, login-token, move-user) overwrites userWorkspace.aventoraEngagementInitiatorPhone. Omitted or empty does not clear. Use PROVISIONING_SECRET, not the workspace API key. | | 2026-09-12 | Reusable SSO token: login-token is identity, not a page ticket. Response includes workspaceOrigin. Partners mint once, then postMessage navigate or rewrite returnToPath until expiresAt. Laravel plugin caches per user+workspace. /verify reuses a matching CRM session instead of clearing cookies. | | 2026-09-09 | CRM video tutorials: Linked the 44 end-user walkthroughs at CRM video tutorials. | | 2026-09-09 | Reable Real Estate v1.1.0: DRAFT Portal Lead / Seller Valuation workflows (person.created + Website form / Home valuation filters) and manual Victoria Day / Family Day / Thanksgiving / Easter blasts. Adds Home valuation lead source. Existing Reable workspaces pick these up via workspace:reapply-industry-preset. Does not ingest portal or valuation forms and does not compute floating holiday dates. | | 2026-09-09 | Reable Real Estate preset: New industry reable_real_estate (alias reable-real-estate) extends stock real estate with extra lead sources, Mutual Release on buyer/seller, Pre-Con and Assignment Seller pipelines, and verbatim Engage packs. Apply with workspace:apply-industry-preset --industry-preset reable_real_estate --force. Profile default is optional. Stock real_estate tenants are unchanged. | | 2026-09-07 | Docs audiences: End-user CRM work lives in the CRM User Guide. This complete guide remains the administrator, operator, and integrator reference. | | 2026-08-28 | People spreadsheet import matching: Upsert identity is email, then phone. First + last name is used only when the imported row has neither. Duplicate emails in the file are not blocked. | | 2026-08-23 | Bulk email by Person view: GET /rest/aventora/person-views lists Person views. POST /rest/aventora/start-engagement-bulk accepts viewId (mutually exclusive with personIds and "audience": "all"). See Outbound Email Templates. | | 2026-08-23 | Rotate partner API key: POST /auth/provision/rotate-api-key revokes Partner Provisioning keys and returns a new twentyApiKey. Same PROVISIONING_SECRET as resolve. Laravel Crm::rotateWorkspaceApiKey() fires CrmWorkspaceApiKeyRotated. | | 2026-08-23 | Query / Build using AI eligibility: SMS and phone require a phone number; email requires an email; date audiences require that date (birthday = Date of Birth, month and day). Missing data skips the person; empty audience skips the run. Applies to every query outreach and record-triggered send, not only birthday texts. Lookup no longer fails with Property "phones" was not found in "person". | | 2026-08-23 | Host env file permissions: Restrict the Compose .env on the VPS (chmod 600; typical path /opt/aventora/crm/.env). Do not commit live secrets. | | 2026-08-21 | Build using AI: Optional workflow-editor overlay (server env USE_AI, default off). Users describe automations in everyday language; the builder applies Aventora defaults (birthday texts use Date of Birth / BIRTHDAY_TODAY) and does not quiz on API names. Writes a draft on this workflow. Does not enable Twenty Ask AI (IS_AI_ENABLED stays off). | | 2026-08-21 | Admin 2FA: Server env USE_2FA (default true) requires workspace admins to enable and enter 2FA at login. Set USE_2FA=false to skip that enforcement. Compose passes the flag into server/worker. | | 2026-08-21 | Workspace owner is Admin: Laravel adminEmail and the TIPS workspace-owner agent get the standard Admin role (full settings). Extra SSO users stay Aventora User. Platform admin remains Admin. Backfill: workspace:backfill-provisioned-workspace-admins (also runs on Docker startup). | | 2026-08-20 | Workflow command menu: Preset workflow labels no longer show the internal [preset:av_wf_…] key. Users see Bulk SMS: Reconnect, not [preset:av_wf_bulk_reconnect] Bulk SMS: Reconnect. | | 2026-08-20 | CRM workflow templates: Birthday, four fixed-date holiday blasts, and Thank You After Meeting seed as DRAFT on every industry. Insurance, real estate, mortgage, and financial advisor also get industry-specific DRAFT workflows. Birthday needs Date Of Birth filled (insDateOfBirth or avDateOfBirth). Holiday audience is capped at 200 and skips do-not-contact / missing phone. Cron is 14:00 UTC; activate only the holidays that apply. Existing workspaces pick these up via workspace:reapply-industry-preset. | | 2026-08-20 | Voice memos: New notes/tasks default to Voice Note / Voice Task plus the date and time. Custom titles are not overwritten. | | 2026-08-20 | Voice-memo transcription: Docker Compose passes OPENAI_API_KEY into the CRM server/worker. When the key is unset, Notes/Tasks Transcribe is disabled. | | 2026-08-20 | Engage by Aventora disabled reason: Quick/bulk toolbar actions stay visible when blocked. Hover and click explain missing callback number (Settings → Profile), missing AVENTORA_ENGAGEMENT permission, or Hub not connected. | | 2026-08-20 | Bulk email to all contacts: POST /rest/aventora/start-engagement-bulk accepts "audience": "all" (mutually exclusive with personIds). See Outbound Email Templates. | | 2026-08-20 | Partner workspace API key: POST /auth/provision/resolve returns twentyApiKey (Admin role) only when a new workspace is created. Store it server-side; Laravel GET /crm/sso omits it and fires CrmWorkspaceProvisioned. | | 2026-08-18 | Claude Custom Connectors: CRM POST /mcp returns HTTP 200 (not Nest’s default 201) so Claude can read tool results. See CRM MCP. | | 2026-08-18 | Claude Custom Connectors: CRM /mcp named tools run as the user who approved OAuth. See CRM MCP. | | 2026-08-18 | Claude Custom Connectors: CRM /mcp accepts Application OAuth tokens (mcp:tools) in addition to workspace API keys. See CRM MCP. | | 2026-08-18 | CRM MCP: POST /mcp is an Aventora-owned named-tool server (People/Notes). Cursor snippet is under Settings → AI. See CRM MCP. | | 2026-08-18 | /crm URL: docs.aventora.ai/crm redirects to this complete guide so in-app Documentation links resolve. | | 2026-08-18 | In-app help: CRM pages and panels include an info icon for English contextual help. Documentation nav opens docs.aventora.ai/crm; in-app /docs one-pager removed. | | 2026-08-17 | Workflows — Aventora Engagement: Cockpit action type is a dropdown. Instruction is a multiline field prefilled from the selected action and can be edited. | | 2026-08-17 | Insurance preset v1.2.0: Broader Client fields (kids counts, source/status/market, commercial/recruiting fields). Reapply adds missing SELECT options (Couple) and deactivates Dependents. People spreadsheet import maps Email 2 / Phone 2 and Note/Task columns. | | 2026-08-11 | Partner iframe embeds: CRM FRAME_ANCESTORS env — unset = DENY (staging); set = CSP allowlist for partner host origins. | | 2026-08-09 | Chatbot superadmin ensure: platform bootstrap uses superadmin ensure-super-admin so platform/shared CHATBOT_SUPER_ADMIN_PASSWORD creates or reconciles the DB hash (re-run bootstrap to recover drift). API-key / DOMAIN_CHATBOT_SUPER_ADMIN_TOKEN health does not prove password-login alignment. | | 2026-08-09 | CRM Hub provision Twilio env: stamps telephony from Hub runtime TWILIO_ACCOUNT_SID / TWILIO_AUTH_TOKEN / TWILIO_SMS_NUMBER (or phone number / API key pair). Separate PROVISION_* Twilio vars are optional overrides only — admin Add Domain and CRM workspace auto-provision share the same credentials. | | 2026-08-09 | AWS Hub CRM provision auth: staging Hub injects DOMAIN_CHATBOT_SUPER_ADMIN_TOKEN from the bootstrap-managed Chatbot PLATFORM_ADMIN_API_KEY (same Secrets Manager JSON key). Username/password remain a VPS/Compose / superadmin ensure-super-admin path; Chatbot super-admin routes accept JWT or platform-wide API key. | | 2026-07-28 | White-label brand name: optional server env WHITE_LABEL (Docker .env → Compose) sets the partner display name for CRM UI labels, user-facing API messages, and transactional emails. Unset/blank keeps Aventora / Aventora CRM. Injected into the SPA via window._env_. Logos and aventora.ai URLs are unchanged. | | 2026-07-22 | Workspace demo seed cleanup: Docker startup runs workspace:remove-standard-objects-prefill unless SHOULD_PREFILL_STANDARD_OBJECTS=true. Soft-deletes Twenty’s fixed demo UUIDs only (Airbnb/etc companies, people, opportunities, Quick Lead, My First Dashboard); does not touch user or integration data. Pass --destroy for permanent deletion. | | 2026-07-22 | Workspace activation: demo CRM sample data (companies/people/opportunities/Quick Lead/dashboard) is no longer loaded by default. Opt in with server env SHOULD_PREFILL_STANDARD_OBJECTS=true. | | 2026-07-19 | Contact record tabs: when Person has listings / policies relations (Real Estate / Insurance presets), the CRM shows dedicated Listings / Policies tabs as the second tab after Timeline, with an Add button to create related records. | | 2026-07-19 | Industry presets reapply: workspace:reapply-industry-preset falls back to the industry default profile when a workspace has industryPreset but no industryProfile (common on older real_estate tenants), then persists that profile. | | 2026-07-19 | Industry presets v1.1.0: Real Estate seeds a Listing custom object (Contact → Listings); Insurance renames Opportunity → Quote and seeds a Policy custom object (Client → Policies). New PresetCustomObjectsApplier; existing workspaces pick this up via workspace:reapply-industry-preset. | | 2026-07-17 | Add Domain telephony: auto-purchased Twilio numbers now set inbound SmsUrl to {TWILIO_WEBHOOK_BASE_URL}/sms/webhook (plus existing VoiceUrl). Missing SmsUrl caused Hub to miss customer SMS replies while delivery status still worked. | | 2026-07-14 | Embedded SSO: Laravel plugin again sends embedded: true whenever page is set (hides CRM left nav). Direct login-token still needs explicit "embedded": true; page alone only sets returnToPath (Hub Call Logs). | | 2026-07-14 | Default phone country: optional server env DEFAULT_PHONE_COUNTRY_CODE (ISO, e.g. US) falls back when workspace Phones field Default Country Code is unset; exposed via client-config; workspace setting still overrides. | | 2026-07-12 | Industry presets: replaced the truncated "example profiles" list with the full catalog (all industryProfile values, default profile, and which presets require a profile); documented general/other → generic aliases and 400 behavior. | | 2026-07-12 | Industry presets: corrected stale note — POST /auth/provision/resolve does accept industryPreset / industryProfile; they apply only when resolve creates a new workspace (new subdomain or personal), matching Laravel Integration. | | 2026-07-12 | Industry presets: applying or re-applying a preset now also renames reciprocal relation field labels when Person, Company, or Opportunity object labels change (for example person.company follows the Company singular label). | | 2026-07-05 | Campaign landing pages: Hub /campaign/{token} uses TWENTY_BASE_URL / CRM_API_URL for CRM public config and submit. | | 2026-07-05 | SSO deep links: login-token optional embedded: true adds aventoraEmbedded=1 (hide nav). page alone sets returnToPath only — use for new-tab person links from Engagement Hub Call Logs. | | 2026-07-05 | Campaign landing pages: CRM Campaigns UI (/campaigns), Hub /campaign/{token} form, public submit API, tag-only vs auto-engage. See Campaign landing pages. | | 2026-07-02 | Person ownership vs CRM sync: documented that bulk Hub outreach can sync contacts/engagements without ownership context; Hub uses account email fallback when domain-user email is missing. | | 2026-07-02 | Add Domain telephony (test mode): domain-chatbot TWILIO_PROVISION_TEST_MODE=true enables end-to-end auto-provision with Twilio test credentials (magic number +15005550006, synthetic API key). Use live master credentials with this flag off for real purchases. | | 2026-07-02 | Add Domain telephony: optional Twilio setup in aventora-admin (skip, manual API key, or auto-provision). provision-domain accepts API key auth only; classic auth token remains Account Settings only. |