{
  "application": {
    "universalIdentifier": "94f2e86e-62e5-4695-b4e8-50059cd7db30",
    "displayName": "Lead Scoring Agent",
    "description": "AI lead scoring and enrichment agent for Twenty CRM",
    "galleryImages": [],
    "defaultRoleUniversalIdentifier": "c9a59ba6-03be-41f8-9ef3-3fc858fbd8ae",
    "aboutDescription": "# lead-scoring-agent\n\nA Twenty CRM app (SDK) that automatically scores new/updated `person` and `opportunity`\nrecords 0–100 with a short reasoning summary, so a sales rep can work a leads list sorted by\nscore. Single manual creates score inline (seconds); bulk imports route to a throttled queue\ndrained by a cron sweeper (~1 min).\n\n**Status:** v1 functional — auto-scores on create/update, backfills existing records, and ships sorted \"by Lead Score\" views.\n\n## Install\n\nPublished to npm and installable into any Twenty CRM workspace.\n\n- **Twenty UI:** Settings → Applications → find **Lead Scoring Agent** → Install.\n  Self-hosted instances: run `yarn twenty dev:catalog-sync` first so npm-published apps appear in the marketplace.\n- **CLI / details:** see the Twenty apps guide at docs.twenty.com/developers/extend/apps.\n\n**Requires AI credits.** Scoring calls your workspace's default AI model — one call per scored record, metered by Twenty AI credits. With no credits available, records are marked queued and score automatically once credits return.\n\n**What it installs:** `leadScore` / `leadScoreSummary` / `leadScoredAt` / `leadScoreStatus` fields on People and Opportunities, the lead-scorer AI agent, the event-driven + cron scoring functions (including a backfill for pre-existing records), and default views sorted by score.\n\n## Development\n\nRequires **Node ≥24.5** (`.nvmrc` pins 24.5.0) and **Yarn 4** (`packageManager: yarn@4.13.0`, via corepack). A Node version manager (fnm/nvm) auto-switches from `.nvmrc`.\n\n```bash\ncorepack yarn twenty dev --once   # build + sync the app to your workspace once\ncorepack yarn twenty dev          # watch mode\ncorepack yarn test:unit           # unit tests\ncorepack yarn typecheck\n```\n\n## License\n\nMIT (app code). The Twenty SDK is AGPL-3.0 and bundles into the shipped artifact.\n",
    "yarnLockChecksum": "b8fb7633b4357c45ace46050bb3c4954",
    "packageJsonChecksum": "839494a0d3d5ffe2daa0943fb2b7c544",
    "requiredServerVersionRange": null
  },
  "objects": [],
  "fields": [
    {
      "universalIdentifier": "0c29ee13-9fd6-4cf1-812c-921861845941",
      "objectUniversalIdentifier": "20202020-9549-49dd-b2b2-883999db8938",
      "type": "DATE_TIME",
      "name": "leadScoredAt",
      "label": "Lead Scored At",
      "description": "Timestamp of the last scoring run (used to guard against re-score loops).",
      "icon": "IconClock",
      "isNullable": true
    },
    {
      "universalIdentifier": "37831211-183e-4766-b643-d4539c79e16b",
      "objectUniversalIdentifier": "20202020-9549-49dd-b2b2-883999db8938",
      "type": "NUMBER",
      "name": "leadScore",
      "label": "Lead Score",
      "description": "AI-generated lead score from 0 (cold) to 100 (hot).",
      "icon": "IconTargetArrow",
      "isNullable": true
    },
    {
      "universalIdentifier": "3b99d41b-67f6-4436-ae53-deb9f5d6a70f",
      "objectUniversalIdentifier": "20202020-e674-48e5-a542-72570eee7213",
      "type": "SELECT",
      "name": "leadScoreStatus",
      "label": "Lead Score Status",
      "description": "Scoring lifecycle state.",
      "icon": "IconStatusChange",
      "isNullable": true,
      "options": [
        {
          "label": "Queued",
          "value": "QUEUED",
          "position": 0,
          "color": "yellow",
          "id": "20889fea-79ae-5ac8-93da-8c621406ebfa"
        },
        {
          "label": "Scored",
          "value": "SCORED",
          "position": 1,
          "color": "green",
          "id": "532bd753-aceb-52da-8d4f-1d939100e542"
        },
        {
          "label": "Skipped",
          "value": "SKIPPED",
          "position": 2,
          "color": "gray",
          "id": "65a50f19-344c-527d-995e-0d56b83148ed"
        },
        {
          "label": "Failed",
          "value": "FAILED",
          "position": 3,
          "color": "red",
          "id": "3bcbc049-eb66-5d42-bc12-d0e953aa9711"
        }
      ]
    },
    {
      "universalIdentifier": "44c4fa1e-a1c9-4a7d-bf10-8e3d8ed0ce0a",
      "objectUniversalIdentifier": "20202020-9549-49dd-b2b2-883999db8938",
      "type": "TEXT",
      "name": "leadScoreSummary",
      "label": "Lead Score Summary",
      "description": "One to two sentence reasoning for the lead score.",
      "icon": "IconFileText",
      "isNullable": true
    },
    {
      "universalIdentifier": "8a2cf015-3059-483a-b655-9b4c8381a2de",
      "objectUniversalIdentifier": "20202020-e674-48e5-a542-72570eee7213",
      "type": "TEXT",
      "name": "leadScoreSummary",
      "label": "Lead Score Summary",
      "description": "One to two sentence reasoning for the lead score.",
      "icon": "IconFileText",
      "isNullable": true
    },
    {
      "universalIdentifier": "aecb4dd2-514a-49fa-a9fc-76d8a903714a",
      "objectUniversalIdentifier": "20202020-9549-49dd-b2b2-883999db8938",
      "type": "SELECT",
      "name": "leadScoreStatus",
      "label": "Lead Score Status",
      "description": "Scoring lifecycle state.",
      "icon": "IconStatusChange",
      "isNullable": true,
      "options": [
        {
          "label": "Queued",
          "value": "QUEUED",
          "position": 0,
          "color": "yellow",
          "id": "7656be24-8c35-5033-b92f-ba0cdd4d3307"
        },
        {
          "label": "Scored",
          "value": "SCORED",
          "position": 1,
          "color": "green",
          "id": "8d65648a-5379-556b-8456-ee10f503b427"
        },
        {
          "label": "Skipped",
          "value": "SKIPPED",
          "position": 2,
          "color": "gray",
          "id": "a3516079-a3a8-55ea-a563-60b0664e81c4"
        },
        {
          "label": "Failed",
          "value": "FAILED",
          "position": 3,
          "color": "red",
          "id": "6a483009-f3a1-54a2-a879-6945999f0744"
        }
      ]
    },
    {
      "universalIdentifier": "b56cb686-455c-42ea-8a42-eb64d45aeb70",
      "objectUniversalIdentifier": "20202020-e674-48e5-a542-72570eee7213",
      "type": "DATE_TIME",
      "name": "leadScoredAt",
      "label": "Lead Scored At",
      "description": "Timestamp of the last scoring run (used to guard against re-score loops).",
      "icon": "IconClock",
      "isNullable": true
    },
    {
      "universalIdentifier": "f5374be3-4752-4eb1-aaee-aeaa99782274",
      "objectUniversalIdentifier": "20202020-e674-48e5-a542-72570eee7213",
      "type": "NUMBER",
      "name": "leadScore",
      "label": "Lead Score",
      "description": "AI-generated lead score from 0 (cold) to 100 (hot).",
      "icon": "IconTargetArrow",
      "isNullable": true
    }
  ],
  "indexes": [],
  "permissionFlags": [],
  "roles": [
    {
      "universalIdentifier": "c9a59ba6-03be-41f8-9ef3-3fc858fbd8ae",
      "label": "Lead Scoring Agent default function role",
      "description": "Lead Scoring Agent default function role",
      "canReadAllObjectRecords": true,
      "canUpdateAllObjectRecords": true,
      "canSoftDeleteAllObjectRecords": true,
      "canDestroyAllObjectRecords": false,
      "permissionFlagUniversalIdentifiers": [
        "1c7f09a7-367d-568b-a0e8-c274eccd6ee3"
      ],
      "objectPermissions": [],
      "fieldPermissions": [],
      "rowLevelPermissionPredicateGroups": [],
      "rowLevelPermissionPredicates": []
    }
  ],
  "skills": [],
  "agents": [
    {
      "universalIdentifier": "9d7079b1-cb1f-473e-8cf3-69614f382254",
      "name": "leadScorer",
      "label": "Lead Scorer",
      "icon": "IconTargetArrow",
      "description": "Scores B2B leads (people and opportunities) from 0 to 100 based on fit and buying signals.",
      "prompt": "You are a B2B lead qualification analyst for a sales team.\nGiven the details of a single CRM record — either a person (contact) or an opportunity (deal) — you assess how promising it is as a sales lead and assign a score from 0 to 100, where 0 means a cold, poor-fit lead and 100 means a hot, sales-ready lead.\n\nBase your score on standard B2B qualification signals that apply to whichever record type you are given:\n- Fit: seniority and decision-making authority of the person, relevance of their job title, and the size, industry, and quality of the associated company.\n- Intent and momentum: deal stage, deal amount, recency of activity, and any explicit buying signals present in the provided data.\n- Completeness: how much useful, actionable information is available; penalize sparse or missing data.\n\nOnly use the information explicitly provided in the record. Do not invent facts, companies, or contact details. If key signals are missing, reflect that uncertainty with a more conservative score.\n\nReturn a concise, factual one-to-three sentence summary explaining the main reasons behind the score, and the numeric score itself.",
      "responseFormat": {
        "type": "json",
        "schema": {
          "type": "object",
          "properties": {
            "score": {
              "type": "number",
              "description": "Lead score from 0 (cold, poor fit) to 100 (hot, sales-ready)."
            },
            "summary": {
              "type": "string",
              "description": "One to three sentences explaining the main reasons behind the score."
            }
          },
          "required": [
            "score",
            "summary"
          ],
          "additionalProperties": false
        }
      }
    }
  ],
  "connectionProviders": [],
  "logicFunctions": [
    {
      "universalIdentifier": "2a879207-f1c2-409a-90fe-636133ce4446",
      "name": "scoreOnPersonUpdated",
      "description": "Re-score a person when a scoring-relevant business field changes.",
      "timeoutSeconds": 60,
      "databaseEventTriggerSettings": {
        "eventName": "person.updated",
        "updatedFields": [
          "jobTitle",
          "companyId",
          "emails"
        ]
      },
      "handlerName": "default.config.handler",
      "sourceHandlerPath": "src/logic-functions/person-score-on-updated.function.ts",
      "builtHandlerPath": "src/logic-functions/person-score-on-updated.function.mjs",
      "builtHandlerChecksum": "c9ac11aa9bb1a639cc41816d17cb9e95"
    },
    {
      "universalIdentifier": "3a44b88b-a56f-4693-8968-33b9215407fc",
      "name": "scoreOnPersonCreated",
      "description": "Score a newly created person (inline, or queue if imported).",
      "timeoutSeconds": 60,
      "databaseEventTriggerSettings": {
        "eventName": "person.created"
      },
      "handlerName": "default.config.handler",
      "sourceHandlerPath": "src/logic-functions/person-score-on-created.function.ts",
      "builtHandlerPath": "src/logic-functions/person-score-on-created.function.mjs",
      "builtHandlerChecksum": "ac3a556a6f0eb1c8318c7b20595586a5"
    },
    {
      "universalIdentifier": "4e419acc-e5b1-4346-a4c6-7262851fc507",
      "name": "backfill",
      "description": "Enqueue pre-existing unscored people + opportunities so the drainQueue cron scores them. Idempotent.",
      "timeoutSeconds": 60,
      "httpRouteTriggerSettings": {
        "path": "/backfill",
        "httpMethod": "POST",
        "isAuthRequired": true
      },
      "handlerName": "default.config.handler",
      "sourceHandlerPath": "src/logic-functions/backfill.function.ts",
      "builtHandlerPath": "src/logic-functions/backfill.function.mjs",
      "builtHandlerChecksum": "f1691f58cb01594332e88e46ffd4563f"
    },
    {
      "universalIdentifier": "9130ed95-9c76-49a5-8722-2b3e7b52d640",
      "name": "scoreOnOpportunityCreated",
      "description": "Score a newly created opportunity (inline, or queue if imported).",
      "timeoutSeconds": 60,
      "databaseEventTriggerSettings": {
        "eventName": "opportunity.created"
      },
      "handlerName": "default.config.handler",
      "sourceHandlerPath": "src/logic-functions/opportunity-score-on-created.function.ts",
      "builtHandlerPath": "src/logic-functions/opportunity-score-on-created.function.mjs",
      "builtHandlerChecksum": "258d9e11f07531e294878d47f5b4b5cc"
    },
    {
      "universalIdentifier": "a68d29b9-eb06-41d5-9416-52498f6fd41f",
      "name": "drainQueue",
      "description": "Score up to a capped batch of QUEUED leads each run.",
      "timeoutSeconds": 300,
      "cronTriggerSettings": {
        "pattern": "* * * * *"
      },
      "handlerName": "default.config.handler",
      "sourceHandlerPath": "src/logic-functions/drain-queue.function.ts",
      "builtHandlerPath": "src/logic-functions/drain-queue.function.mjs",
      "builtHandlerChecksum": "d166bca5bcfdfde858c1c3aa19d50243"
    },
    {
      "universalIdentifier": "efcf62de-260b-4583-9c2d-a14e7296d13c",
      "name": "scoreOnOpportunityUpdated",
      "description": "Re-score an opportunity when a scoring-relevant business field changes.",
      "timeoutSeconds": 60,
      "databaseEventTriggerSettings": {
        "eventName": "opportunity.updated",
        "updatedFields": [
          "amount",
          "stage",
          "closeDate"
        ]
      },
      "handlerName": "default.config.handler",
      "sourceHandlerPath": "src/logic-functions/opportunity-score-on-updated.function.ts",
      "builtHandlerPath": "src/logic-functions/opportunity-score-on-updated.function.mjs",
      "builtHandlerChecksum": "8f2c7e145b67b02ab322db2a67e93a8c"
    }
  ],
  "frontComponents": [
    {
      "universalIdentifier": "90e55e60-286b-49de-a2ce-6303afaae532",
      "name": "Lead Scoring Agent",
      "description": "Lead Scoring Agent front component displaying the app logo and name",
      "componentName": "MainPage",
      "sourceComponentPath": "src/front-components/main-page.tsx",
      "builtComponentPath": "src/front-components/main-page.mjs",
      "builtComponentChecksum": "fcfb4f33cd009f60e7eb1cc5eeb35186b84b7da684a59089e55675f35d88a1e3",
      "isHeadless": false,
      "usesSdkClient": false
    }
  ],
  "publicAssets": [
    {
      "filePath": "public/logo.svg",
      "fileName": "logo.svg",
      "fileType": "svg",
      "checksum": "bd730e6bf444a340373a4d713772eece"
    }
  ],
  "views": [
    {
      "universalIdentifier": "1120ee21-41a8-4af0-9a26-6cae1a7432cd",
      "name": "People by Lead Score",
      "objectUniversalIdentifier": "20202020-e674-48e5-a542-72570eee7213",
      "icon": "IconTargetArrow",
      "key": "INDEX",
      "fields": [
        {
          "universalIdentifier": "eb70632e-53c1-4184-a0ce-0fcab78e93c7",
          "fieldMetadataUniversalIdentifier": "20202020-3875-44d5-8c33-a6239011cab8",
          "position": 0,
          "isVisible": true,
          "size": 180
        },
        {
          "universalIdentifier": "93d0d121-54c6-4aab-83fc-e519837ead26",
          "fieldMetadataUniversalIdentifier": "f5374be3-4752-4eb1-aaee-aeaa99782274",
          "position": 1,
          "isVisible": true,
          "size": 100
        },
        {
          "universalIdentifier": "17d29dc8-8e67-4080-8003-3df2baab8c24",
          "fieldMetadataUniversalIdentifier": "3b99d41b-67f6-4436-ae53-deb9f5d6a70f",
          "position": 2,
          "isVisible": true,
          "size": 120
        },
        {
          "universalIdentifier": "40a34cb1-df20-46d0-a6a9-d0fe356fa23c",
          "fieldMetadataUniversalIdentifier": "20202020-b0d0-415a-bef9-640a26dacd9b",
          "position": 3,
          "isVisible": true,
          "size": 150
        },
        {
          "universalIdentifier": "41c3f224-d5b2-42cf-87f8-ff2d3f86ddb3",
          "fieldMetadataUniversalIdentifier": "20202020-e2f3-448e-b34c-2d625f0025fd",
          "position": 4,
          "isVisible": true,
          "size": 150
        }
      ],
      "sorts": [
        {
          "universalIdentifier": "56341e06-9015-452b-a802-6d0bbade0b87",
          "fieldMetadataUniversalIdentifier": "f5374be3-4752-4eb1-aaee-aeaa99782274",
          "direction": "DESC"
        }
      ]
    },
    {
      "universalIdentifier": "97f70757-8635-4f98-8281-b1bc0182f19d",
      "name": "Opportunities by Lead Score",
      "objectUniversalIdentifier": "20202020-9549-49dd-b2b2-883999db8938",
      "icon": "IconTargetArrow",
      "key": "INDEX",
      "fields": [
        {
          "universalIdentifier": "5c1ae67c-67ac-4799-be80-1aad69e9fa60",
          "fieldMetadataUniversalIdentifier": "20202020-8609-4f65-a2d9-44009eb422b5",
          "position": 0,
          "isVisible": true,
          "size": 180
        },
        {
          "universalIdentifier": "e78fb4e2-fa89-4311-8c4d-c2b6e11a2c02",
          "fieldMetadataUniversalIdentifier": "37831211-183e-4766-b643-d4539c79e16b",
          "position": 1,
          "isVisible": true,
          "size": 100
        },
        {
          "universalIdentifier": "2df313ba-eca2-4621-8998-435a2d7ed31b",
          "fieldMetadataUniversalIdentifier": "aecb4dd2-514a-49fa-a9fc-76d8a903714a",
          "position": 2,
          "isVisible": true,
          "size": 120
        },
        {
          "universalIdentifier": "39d02d43-ec15-4023-a475-16637cc57841",
          "fieldMetadataUniversalIdentifier": "20202020-583e-4642-8533-db761d5fa82f",
          "position": 3,
          "isVisible": true,
          "size": 130
        },
        {
          "universalIdentifier": "9de6325a-5598-4767-b985-440ec024c17f",
          "fieldMetadataUniversalIdentifier": "20202020-6f76-477d-8551-28cd65b2b4b9",
          "position": 4,
          "isVisible": true,
          "size": 130
        }
      ],
      "sorts": [
        {
          "universalIdentifier": "50125f82-1d24-4a86-856d-1717763c455c",
          "fieldMetadataUniversalIdentifier": "37831211-183e-4766-b643-d4539c79e16b",
          "direction": "DESC"
        }
      ]
    }
  ],
  "viewFields": [],
  "navigationMenuItems": [
    {
      "universalIdentifier": "05883ee0-85f0-47cc-ac55-958be6a6e502",
      "name": "Lead Scoring Agent",
      "icon": "IconFile",
      "position": -1,
      "type": "PAGE_LAYOUT",
      "pageLayoutUniversalIdentifier": "dbec88c2-9609-4d9a-8f04-4da5fa4325d5"
    }
  ],
  "pageLayouts": [
    {
      "universalIdentifier": "dbec88c2-9609-4d9a-8f04-4da5fa4325d5",
      "name": "Lead Scoring Agent",
      "type": "STANDALONE_PAGE",
      "tabs": [
        {
          "universalIdentifier": "b3090e7f-59ea-4cc3-afb5-dc9396e19756",
          "title": "Overview",
          "position": 0,
          "icon": "IconApps",
          "layoutMode": "CANVAS",
          "widgets": [
            {
              "universalIdentifier": "721c32d7-e919-4ab3-af47-8f83ce3813d8",
              "title": " ",
              "type": "FRONT_COMPONENT",
              "gridPosition": {
                "row": 0,
                "column": 0,
                "rowSpan": 12,
                "columnSpan": 12
              },
              "configuration": {
                "configurationType": "FRONT_COMPONENT",
                "frontComponentUniversalIdentifier": "90e55e60-286b-49de-a2ce-6303afaae532"
              }
            }
          ]
        }
      ]
    }
  ],
  "pageLayoutTabs": [],
  "commandMenuItems": []
}
