8.5 KiB
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
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:
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:
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
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)
{
"input": {
"field": {
"name": "debtor",
"label": "Debtor",
"type": "RELATION",
"objectMetadataId": "<SOURCE_OBJECT_UUID>",
"relationCreationPayload": {
"type": "MANY_TO_ONE",
"targetObjectMetadataId": "<TARGET_OBJECT_UUID>",
"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
targetFieldLabelandtargetFieldIconcontrol the reverse field's label/icon- The source-side
settingswill 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
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
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\"]}')
"