Initial skills documentation — 25 categories, all SKILL.md + references + scripts
This commit is contained in:
+249
@@ -0,0 +1,249 @@
|
||||
# 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": "<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
|
||||
- `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\"]}')
|
||||
"
|
||||
```
|
||||
Reference in New Issue
Block a user