# TwentyCRM DRE Custom Data Model Created 2026-07-08 via `https://crm.debtrecoveryexperts.com/rest/metadata/` REST API. Auth: Bearer token from `TWENTY_CRM_API_KEY` in `~/.hermes/.env`. ## Objects ### 1. Debtors (`debtor`/`debtors`) - **Icon:** IconUsers - **Label Identifier:** Name (auto-created TEXT field) - **Custom fields:** | Field | Type | Icon | Description | |-------|------|------|-------------| | contactInfo | TEXT | IconPhone | Primary contact phone number | | email | TEXT | IconMail | Primary email address | | physicalAddress | TEXT | IconMapPin | Physical address | | businessType | SELECT | IconBuildingStore | INDIVIDUAL, SOLE_PROPRIETORSHIP, LLC, CORPORATION, PARTNERSHIP, OTHER | ### 2. Claims (`claim`/`claims`) - **Icon:** IconFiles - **Label Identifier:** Name (auto-created TEXT field) - **Custom fields:** | Field | Type | Icon | Description | |-------|------|------|-------------| | claimNumber | TEXT (unique) | IconHash | DRE-YYYY-NNNN format | | amount | CURRENCY | IconCurrencyDollar | Claim amount in USD | | status | SELECT | IconStatusChange | NEW, ACTIVE, NEGOTIATION, LEGAL, SETTLED, CLOSED, WRITE_OFF | | tier | SELECT | IconLayersSelected | TIER_1, TIER_2, TIER_3 | | clientReference | TEXT | IconFileDescription | Client reference number | | dateAssigned | DATE_TIME | IconCalendarPlus | Date claim was assigned | | dateResolved | DATE_TIME | IconCalendarCheck | Date claim was resolved | ### 3. Case Notes (`caseNote`/`caseNotes`) - **Icon:** IconNotes - **Label Identifier:** Name (auto-created TEXT field) - **Custom fields:** | Field | Type | Icon | Description | |-------|------|------|-------------| | content | RICH_TEXT | IconFileText | Note content | | author | TEXT | IconUser | Note author name | ### 4. Payments (`payment`/`payments`) - **Icon:** IconReceipt - **Label Identifier:** Name (auto-created TEXT field) - **Custom fields:** | Field | Type | Icon | Description | |-------|------|------|-------------| | paymentAmount | CURRENCY | IconCurrencyDollar | Amount received | | payer | TEXT | IconUser | Name of payer | | paymentDate | DATE_TIME | IconCalendar | Date payment received | | paymentMethod | SELECT | IconCreditCard | CHECK, WIRE_TRANSFER, CREDIT_CARD, CASH, ACH, OTHER | ## REST API Field Creation Pattern ```bash API_KEY=$(grep TWENTY_CRM_API_KEY ~/.hermes/.env | cut -d= -f2) # Create object curl -s "https://crm.debtrecoveryexperts.com/rest/metadata/objects" \ -X POST \ -H "Content-Type: application/json" \ -H "Authorization: Bearer $API_KEY" \ -d '{ "nameSingular": "myObject", "namePlural": "myObjects", "labelSingular": "My Object", "labelPlural": "My Objects", "description": "...", "icon": "IconFiles", "isSearchable": true, "isActive": true, "isLabelSyncedWithName": true }' ``` **Create a regular field:** ```bash curl -s "https://crm.debtrecoveryexperts.com/rest/metadata/fields" \ -X POST \ -H "Content-Type: application/json" \ -H "Authorization: Bearer $API_KEY" \ -d '{ "name": "fieldName", "label": "Field Name", "description": "...", "type": "TEXT", "icon": "IconMail", "isNullable": true, "isUnique": false, "objectMetadataId": "'"$OBJECT_ID"'" }' ``` **Create a SELECT field with options:** ```bash curl -s "https://crm.debtrecoveryexperts.com/rest/metadata/fields" \ -X POST \ -H "Content-Type: application/json" \ -H "Authorization: Bearer $API_KEY" \ -d '{ "name": "status", "label": "Status", "description": "...", "type": "SELECT", "icon": "IconStatusChange", "isNullable": true, "isUnique": false, "objectMetadataId": "'"$OBJECT_ID"'", "options": [ {"label": "New", "value": "NEW", "color": "blue", "position": 0}, {"label": "Active", "value": "ACTIVE", "color": "turquoise", "position": 1} ] }' ``` **Available Colors for SELECT options:** blue, turquoise, green, yellow, orange, red, purple, pink, grey, sky ## Relations (MANY_TO_ONE → ONE_TO_MANY) **IMPORTANT:** The REST API endpoint `POST /rest/metadata/fields` does NOT accept `relationCreationPayload` — it returns `"Relation creation payload is required"`. Relations MUST be created via the **GraphQL mutation endpoint** at `POST https://crm.debtrecoveryexperts.com/metadata`. ### GraphQL Mutation for RELATION Fields ```graphql mutation CreateOneField($input: CreateOneFieldMetadataInput!) { createOneField(input: $input) { id name label type settings relation { type sourceObjectMetadata { id nameSingular namePlural } targetObjectMetadata { id nameSingular namePlural } sourceFieldMetadata { id name } targetFieldMetadata { id name } } object { id nameSingular } } } ``` ### Variables (JSON) ```json { "input": { "field": { "name": "debtor", "label": "Debtor", "type": "RELATION", "objectMetadataId": "", "relationCreationPayload": { "type": "MANY_TO_ONE", "targetObjectMetadataId": "", "targetFieldLabel": "Claims", "targetFieldIcon": "IconFileDescription" } } } } ``` ### `relationCreationPayload` Fields | Field | Type | Required | Description | |-------|------|----------|-------------| | `type` | `"MANY_TO_ONE"` \| `"ONE_TO_MANY"` | ✅ | Direction of the relation | | `targetObjectMetadataId` | UUID string | ✅ | The object this relation points to | | `targetFieldLabel` | string (max 63 chars) | ✅ | Label for the auto-generated reverse field on the target object | | `targetFieldIcon` | string | ✅ | Icon name for the reverse field (e.g. `"IconCurrencyDollar"`) | ### How It Works **MANY_TO_ONE** field on SourceObject → TargetObject: - A foreign-key column (e.g. `debtorId`) is **automatically created** on the SourceObject table - A reverse **ONE_TO_MANY** relation field is **automatically created** on the TargetObject - `targetFieldLabel` and `targetFieldIcon` control the reverse field's label/icon - The source-side `settings` will include `{"joinColumnName": "debtorId"}` **ONE_TO_MANY** field on SourceObject → TargetObject: - **No** join column on SourceObject (the FK lives on the other side) - A reverse **MANY_TO_ONE** relation field (with join column) is created on TargetObject ### curl Example ```bash API_KEY=$(grep TWENTY_CRM_API_KEY ~/.hermes/.env | cut -d= -f2) # Create a MANY_TO_ONE relation: Payments → Claim curl -s "https://crm.debtrecoveryexperts.com/metadata" \ -X POST \ -H "Content-Type: application/json" \ -H "Authorization: Bearer $API_KEY" \ -d '{ "query": "mutation CreateOneField($input: CreateOneFieldMetadataInput!) { createOneField(input: $input) { id name type relation { type targetFieldMetadata { id name } targetObjectMetadata { id nameSingular } } } }", "variables": { "input": { "field": { "name": "claim", "label": "Claim", "type": "RELATION", "objectMetadataId": "PAYMENTS_OBJECT_UUID", "relationCreationPayload": { "type": "MANY_TO_ONE", "targetObjectMetadataId": "CLAIMS_OBJECT_UUID", "targetFieldLabel": "Payments", "targetFieldIcon": "IconCurrencyDollar" } } } } }' ``` ### DRE Data Model Relations Table | Source Object | Field | Relation Type | Target Object | Reverse Field | |---------------|-------|---------------|---------------|---------------| | Claims | `debtor` (MANY_TO_ONE) | MANY_TO_ONE | Debtors | `claims` (ONE_TO_MANY) | | CaseNotes | `claim` (MANY_TO_ONE) | MANY_TO_ONE | Claims | `caseNotes` (ONE_TO_MANY) | | Payments | `claim` (MANY_TO_ONE) | MANY_TO_ONE | Claims | `payments` (ONE_TO_MANY) | ## Reserved Field Names These names trigger `INVALID_FIELD_INPUT: This name is reserved`: - `address` (and likely other system-level names) Use alternatives like `physicalAddress`, `mailingAddress`, etc. ## Verification Command ```bash API_KEY=$(grep TWENTY_CRM_API_KEY ~/.hermes/.env | cut -d= -f2) curl -s "https://crm.debtrecoveryexperts.com/rest/metadata/objects" \ -H "Authorization: Bearer $API_KEY" > /tmp/all_objects.json python3 -c " import json, sys data = json.load(open('/tmp/all_objects.json')) for obj in data.get('data', []): if not obj['isSystem']: print(f'\\n## {obj[\"labelSingular\"]} ({obj[\"nameSingular\"]})') print(f' ID: {obj[\"id\"]}') for f in obj.get('fields', []): if not f['isSystem']: print(f' - {f[\"label\"]} ({f[\"name\"]}) type={f[\"type\"]}') " ```