Associations API
Link any two records and list a record's associated records. Supported types: leads, opportunities, accounts, contacts, projects, tasks, work_requests; anything else returns 400 Unsupported record type. There is no single RBAC module — the service checks view / edit on the record types involved, and record access on each record. See Associations & Time Entries.
Endpoints
| Method | Endpoint | Description |
|---|---|---|
| GET | /associations/search?type=&q=&limit= | Find records of one type to link |
| GET | /associations/:entityType/:entityId | Associated records grouped by type (manual + derived links) |
| POST | /associations | Link two records |
| DELETE | /associations/:id | Remove a manual link (soft delete) |
List
GET /associations/opportunities/:id
Requires view on the record's type and record access to it (else 404). Returns one group per type that has links, in registry order:
[
{
"type": "contacts",
"noun": "contact",
"hiddenCount": 0,
"records": [
{
"id": "uuid",
"label": "Jane Doe",
"subtitle": "Acme Inc",
"url": "/contacts/uuid",
"associationId": null,
"associationLabel": null,
"via": "Opportunity contact",
"removable": false
}
]
},
{
"type": "work_requests",
"noun": "work request",
"hiddenCount": 1,
"records": [
{
"id": "uuid",
"label": "WR-000042 · Product demo for Acme",
"subtitle": "in_progress",
"url": "/work-requests/uuid",
"associationId": null,
"associationLabel": null,
"via": "Requested on this record",
"removable": false
}
]
}
]
| Field | Meaning |
|---|---|
via | Set when the link comes from the records' own fields (derived); describes where it comes from |
associationId, associationLabel | Set for manual links; pass associationId to DELETE |
removable | true only for manual links |
hiddenCount | Linked records of this type the caller may not open (never returned) |
Link Two Records
POST /associations
{
"sourceType": "work_requests",
"sourceId": "uuid",
"targetType": "contacts",
"targetId": "uuid",
"label": "Demo attendee"
}
- Requires
editpermission on the source type (403otherwise) and view access to both records (404otherwise). - A record cannot be linked to itself (
400). labelis optional, trimmed to 100 characters.- Idempotent: if the pair is already linked in either direction, the existing link is returned.
{
"id": "uuid",
"sourceType": "work_requests",
"sourceId": "uuid",
"targetType": "contacts",
"targetId": "uuid",
"label": "Demo attendee",
"createdBy": "uuid",
"createdAt": "…"
}
Linking writes an audit entry and a record_linked activity on both records.
Remove a Link
DELETE /associations/:id → { "success": true }
Requires edit permission and record access on either linked record (403 otherwise). Only manual links have an id; derived links cannot be removed here. Writes a record_unlinked activity on both records.
Search (Record Picker)
GET /associations/search?type=accounts&q=acme&limit=20
| Param | Description |
|---|---|
type | Registry type to search |
q | At least 2 characters; matched with ILIKE against the type's search columns |
limit | Default 20, max 50 |
Returns [] when the caller lacks view on the type or q is too short. Results are filtered by record access:
[
{ "id": "uuid", "type": "accounts", "label": "Acme Inc", "subtitle": null, "url": "/accounts/uuid" }
]