Entity References in Workflows and Records

Learn how to work with entity references in Ironclad workflows and records, including different approaches for handling entity data in API responses.

Ironclad Public API: Entity References Guide

Overview

Entity references allow you to link entities (counterparties, vendors, customers, etc.) to workflows and records in Ironclad. Ironclad supports multiple entities per contract. This guide covers how to work with entity references across different API endpoints, including the multi-entity entity_references field, and the various approaches for handling entity data in API responses.

Multi-Entity Support

Records API

To support multiple entities per contract, the Records API has a new entity_references property (an array) inside addProperties. This array is the canonical multi-entity field and supersedes the existing singular entity_reference property.

Every Records API GET response carries both fields. The singular entity_reference represents the first entity in the entity_references array. For single-entity contracts, both fields describe the same entity. For multi-entity contracts, the singular shows only the first; the full list is in the array.

What changes for writes (POST / PATCH):

  • If your integration sends entity_reference (the singular), it continues to work. The server treats it as "this contract has one entity" — every write replaces the full entity list with that single entity.
  • If your integration sends entity_references (the array), the server writes the full array. The singular is automatically kept in sync with the first entry so consumers still on the legacy field see the first entity.
  • If your integration sends both in the same payload, the plural takes precedence and the singular is ignored. This makes GET → modify → PATCH round-trip workflows safe — you don't need to strip the legacy field from your payload while migrating.

Important behavior to know about while you migrate: if your integration only writes entity_reference (the singular), every write replaces the full entity list with a single entity. If a user adds additional entities to a contract through the Ironclad UI, your next sync will overwrite them. To preserve the full list, migrate the write path to entity_references. Both fields remain available while you migrate — we recommend migrating reads and writes to entity_references to take advantage of multi-entity support.

Workflows API

Workflows can now have multiple entity fields. You can set multiple entities when launching a workflow, and update or remove them after launch during the review step.

GET /workflows/:id surfaces each entity field under its own attribute key in the attributes object of the response — not as a single entity_reference key.

For many integrations, entity_reference is already the real attribute ID of the first entity slot. For backward compatibility, entity_reference works as an alias for the first entity slot when no field is literally named entity_reference and that slot hasn't already been set by its real attribute ID. To address every entity slot reliably, pull each field's real attribute ID from the workflow schema endpoint.

Both APIs are backward compatible — existing integrations should keep working — but we recommend migrating to entity_references (Records) and to real per-field attribute IDs (Workflows) soon.

Entity Reference Approaches

Ironclad provides two approaches for handling entity references in API responses. These references are returned by workflow and record endpoints, but not by schema endpoints (which use a different format). Both approaches apply to every entity in a multi-entity entity_references array, not just a single entity.

1. Returning Entity Reference (Minimal Information)

Description: The API returns only minimal reference information:

  • recordId - The unique identifier for the entity
  • subTypeId - The relationship type ID that defines the entity's role in the workflow or record (e.g., customer, vendor, counterparty, etc.). This ID corresponds to a relationship type that determines how the entity is used in the context. You can retrieve available relationship types using the Get Relationship Types endpoint.

Example Response:

{
  "entity_reference": [
    {
      "type": "reference",
      "value": {
        "recordId": "06cb35af-bcc5-4aca-a7e8-cc4f4a1035d2",
        "subTypeId": "b9369328-af3a-4120-933b-c3476cc16973"
      }
    }
  ]
}

2. Hydrating Entity in API Response (Detailed Information)

Description: The API includes hydrated entity details:

  • readableId
  • attributes (e.g., pocName, pocEmail, address)

Example Response:

{
  "entity_reference": [
    {
      "type": "reference",
      "value": {
        "recordId": "8e2e25d1-37fa-4d77-804c-a865adebfe83",
        "subTypeId": "b9369328-af3a-4120-933b-c3476cc16973",
        "readableId": "ENTITY-1",
        "attributes": {
          "entity_pocName": "Jane Doe",
          "entity_pocEmail": "[email protected]",
          "entity_address": {
            "type": "address",
            "value": {
              "lines": ["123 Business St"],
              "locality": "San Francisco",
              "region": "CA",
              "postcode": "94105",
              "country": "US"
            }
          },
          "entity_category": "external",
          "entity_businessType": "company"
        }
      }
    }
  ]
}

API Endpoints

1. Reading Entity Information from Workflows

Endpoint: GET /workflows

Required OAuth Scope: public.workflows.readWorkflows

Query Parameters:

ParameterTypeRequiredDefaultDescription
hydrateEntitiesbooleanNofalseInclude full entity details in the response
filterstringNononeFilter response by a formula

Filtering by Entity:

You can filter workflows by entity using the filter parameter with formulas. For example, to find all workflows associated with a specific entity:

curl --request GET \
     --url 'https://na1.ironcladapp.com/public/api/v1/workflows?filter=Equals([entity], "c207c133-1f21-41b0-b6df-6ae01629cb3e")' \
     --header 'Authorization: Bearer YOUR_ACCESS_TOKEN' \
     --header 'accept: application/json'

For more information about available formulas and filtering options, see the Formulas Help Center. The same filter parameter and entity formulas also work on the records endpoint.

Example Request:

curl --request GET \
     --url 'https://na1.ironcladapp.com/public/api/v1/workflows?hydrateEntities=true' \
     --header 'Authorization: Bearer YOUR_ACCESS_TOKEN' \
     --header 'accept: application/json'

Response Code: 200 OK

Example Response:

Workflows will surface each entity under its own attribute key:

{
  "page": 0,
  "pageSize": 20,
  "count": 1,
  "list": [
    {
      "id": "681b91ce943b808e0503eeea",
      "title": "Contract Agreement with Example Corporation",
      "template": "68126d0c48031c7f40ad5e5a",
      "step": "Sign",
      "attributes": {
        "readableId": "IC-5",
        "counterpartyName": "Example Corporation",
        "entity_reference": [
          {
            "recordId": "8e2e25d1-37fa-4d77-804c-a865adebfe83",
            "readableId": "ENTITY-1",
            "namedTypeIds": [
              "b9369328-af3a-4120-933b-c3476cc16973"
            ],
            "attributes": {
              "entity_address": {
                "type": "address",
                "value": {
                  "lines": ["123 Business St"],
                  "locality": "San Francisco",
                  "region": "CA",
                  "postcode": "94105",
                  "country": "US"
                }
              },
              "entity_pocName": "Jane Doe",
              "entity_category": "external",
              "entity_pocEmail": "[email protected]",
              "entity_businessType": "company"
            }
          }
        ],
        "localAnotherEntity": [
          {
            "recordId": "another-record-id",
            "readableId": "ENTITY-2",
            "namedTypeIds": [
              "12345678-1234-1234-1234-123456789123"
            ],
            "attributes": {
              "entity_address": {
                "type": "address",
                "value": {
                  "lines": ["12345 New Street"],
                  "locality": "Indianapolis",
                  "region": "Indiana",
                  "postcode": "46208",
                  "country": "US"
                }
              },
              "entity_pocName": "John Doe",
              "entity_category": "external",
              "entity_pocEmail": "[email protected]",
              "entity_businessType": "company"
            }
          }
        ]
      }
    }
  ]
}

Endpoint: GET /workflows/:id

Required OAuth Scope: public.workflows.readWorkflows

Query Parameters:

ParameterTypeRequiredDefaultDescription
hydrateEntitiesbooleanNofalseInclude full entity details in the response

2. Creating Workflows with Entity References

Endpoint: POST /workflows

Required OAuth Scope: public.workflows.createWorkflows

Entity Mapping Behavior:

When you provide an entity reference during workflow creation, the system will automatically populate any workflow fields that are mapped to entity properties. If you provide a value for a field that is mapped to an entity property, your provided value will take precedence over the entity's value.

Note: The exact mapping behavior depends on your workflow template configuration. Some fields may be automatically populated from entity data, while others may require manual input.

Setting multiple entities: set each entity field by passing its real attribute ID in attributes. You can set multiple entities in a single launch call. Pull each entity field's real attribute ID from the workflow schema endpoint (entities will have the type referenceType). The value you set for the attribute ID should be the entity's readable ID (e.g. "ENTITY-1") or its record UUID. Passing a non-string value returns a 400 Bad Request with error code INVALID_PARAM.

Backwards compatibility:

If you've used our API before, you may have seen documentation referencing a hard-coded entity_reference key as the attribute ID to use for the entity field. Now that workflows can have multiple entities, it is recommended that you always use the schema endpoint to retrieve the correct attribute ID for each entity field in your specific workflow.

For backwards compatibility, the attribute ID entity_reference is still accepted as an alias for the first entity slot on the form, even if that first entity field has a different attribute ID. The alias applies only when:

  • No field is literally named entity_reference
  • The first entity field hasn't already been set by its real attribute ID

If you pass both the real attribute ID and entity_reference for the same slot, the real attribute ID wins. If you pass the same attribute ID twice with different values, the last write wins.

Request Body:

{
  "template": "your-template-id",
  "attributes": {
    "entity_reference": "ENTITY-1",
    "localAnotherEntity": "ENTITY-2",
    "counterpartyName": "Example Company",
    "other_field": "other_value"
  }
}

Example Request:

curl --request POST \
     --url 'https://na1.ironcladapp.com/public/api/v1/workflows' \
     --header 'Authorization: Bearer YOUR_ACCESS_TOKEN' \
     --header 'Content-Type: application/json' \
     --data '{
       "template": "your-template-id",
       "attributes": {
         "entity_reference": "ENTITY-1",
         "counterpartyName": "Example Corporation"
       }
     }'

Response Code: 201 Created

Endpoint: POST /workflows/async

Required OAuth Scope: public.workflows.createWorkflows

Note: The request body format is identical to the synchronous workflow creation endpoint.

3. Updating Workflows with Entity References

Endpoint: PATCH /public/api/v1/workflows/:id/attributes

Required OAuth Scope: public.workflows.updateWorkflows

Note: The workflow must be at the Review step to update attributes. Each entity field is updated or removed independently by its own attribute path — set or remove one entity field without affecting any other entity field on the workflow.

Important: set/remove on an entity field only swaps which entity is linked; it does not re-sync attributes that are mapped from that entity. Set those explicitly in the same request if they need to change.

Setting an Entity Reference:

{
  "updates": [
    {
      "action": "set",
      "path": "entity_reference",
      "value": "ENTITY-1"
    }
  ]
}

Setting Multiple Entity References:

Update more than one entity field in the same request by including an update for each field's attribute path:

{
  "updates": [
    {
      "action": "set",
      "path": "entity_reference",
      "value": "ENTITY-2"
    },
    {
      "action": "set",
      "path": "localAnotherEntity",
      "value": "ENTITY-1"
    }
  ]
}

Removing an Entity Reference:

{
  "updates": [
    {
      "action": "remove",
      "path": "entity_reference"
    }
  ]
}

Response Code: 200 OK

Example Response:

{
  "id": "workflow-id",
  "title": "Updated Workflow Title",
  "step": "Review",
  "attributes": {
    "counterpartyName": "Example Corporation"
  }
}

Example Request:

curl --request PATCH \
     --url 'https://na1.ironcladapp.com/public/api/v1/workflows/:id/attributes' \
     --header 'Authorization: Bearer YOUR_ACCESS_TOKEN' \
     --header 'Content-Type: application/json' \
     --data '{
       "updates": [
         {
           "action": "set",
           "path": "entity_reference",
           "value": "ENTITY-1"
         }
       ]
     }'

Response Code: 200 OK

Example Response:
Note: This is an excerpt from the full response.

{
    "id": "689cd5acfacbcd36740bea21",
    "title": "Contract with Company Solutions Inc.",
    "template": "68126d0c48031c7f40ad5e5a",
    "step": "Review",
    "attributes": {
        "entity_reference": [
            {
                "recordId": "6d5637d5-4534-4b79-920f-c0d51ff93222",
                "subTypeId": "ba53e018-f0ce-4892-868b-0744d7ea96cd"
            }
        ],
        ...
    }
    ...
}

4. Reading Workflow Schemas with Entity Information

Endpoint: GET /public/api/v1/workflow-schemas

Required OAuth Scope: public.workflows.readSchemas

Example Request:

curl --request GET \
     --url 'https://na1.ironcladapp.com/public/api/v1/workflow-schemas' \
     --header 'Authorization: Bearer YOUR_ACCESS_TOKEN' \
     --header 'accept: application/json'

Endpoint: GET /public/api/v1/workflow-schemas/:templateId

Required OAuth Scope: public.workflows.readSchemas

Example Request:

curl --request GET \
     --url 'https://na1.ironcladapp.com/public/api/v1/workflow-schemas/your-template-id' \
     --header 'Authorization: Bearer YOUR_ACCESS_TOKEN' \
     --header 'accept: application/json'

Response Code: 200 OK

Example Response:

A workflow with multiple entity fields lists each one as its own schema entry, keyed by its real attribute ID with the type referenceType:

{
  "id": "your-template-id",
  "name": "Standard Agreement Template",
  "schema": {
    "entity_reference": {
      "type": "referenceType",
      "displayName": "Counterparty",
      "propertyKey": "entity_reference"
    },
    "localAnotherEntity": {
      "type": "referenceType",
      "displayName": "Vendor",
      "propertyKey": "localAnotherEntity"
    },
    "counterpartyName": {
      "type": "string",
      "displayName": "Counterparty Name",
      "propertyKey": "counterpartyName"
    },
    "agreementDate": {
      "type": "date",
      "displayName": "Agreement Date",
      "propertyKey": "agreementDate"
    }
  }
}

5. Reading Entity Information from Records

Endpoint: GET /public/api/v1/records

Required OAuth Scope: public.records.readRecords

Query Parameters:

ParameterTypeRequiredDefaultDescription
hydrateEntitiesbooleanNofalseInclude full entity details in the response
filterstringNononeFilter response by a formula

Filtering by Entity:

You can filter records by entity using the filter parameter with formulas. For example, to find all records associated with a specific entity:

curl --request GET \
     --url 'https://na1.ironcladapp.com/public/api/v1/records?filter=Equals([entity], "c207c133-1f21-41b0-b6df-6ae01629cb3e")' \
     --header 'Authorization: Bearer YOUR_ACCESS_TOKEN' \
     --header 'accept: application/json'

For more information about available formulas and filtering options, see the Formulas Help Center. Entity formulas match provided entity IDs against all entities in a record's entity_references list, not just the first entity.

Example Request:

curl --request GET \
     --url 'https://na1.ironcladapp.com/public/api/v1/records?hydrateEntities=true' \
     --header 'Authorization: Bearer YOUR_ACCESS_TOKEN' \
     --header 'accept: application/json'

Response Code: 200 OK

Example Response:

Every response carries both entity_reference (the first entity) and entity_references (the full array):

{
  "page": 0,
  "pageSize": 20,
  "count": 1,
  "list": [
    {
      "id": "record-id",
      "name": "Sample Contract Record",
      "type": "contract",
      "addProperties": {
        "entity_reference": {
          "type": "reference",
          "value": {
            "recordId": "8e2e25d1-37fa-4d77-804c-a865adebfe83",
            "readableId": "ENTITY-1",
            "attributes": {
              "entity_pocName": "Jane Doe",
              "entity_pocEmail": "[email protected]",
              "entity_category": "external",
              "entity_businessType": "company"
            }
          }
        },
        "entity_references": {
          "type": "array",
          "value": [
            {
              "recordId": "8e2e25d1-37fa-4d77-804c-a865adebfe83",
              "readableId": "ENTITY-1",
              "attributes": {
                "entity_pocName": "Jane Doe",
                "entity_pocEmail": "[email protected]",
                "entity_category": "external",
                "entity_businessType": "company"
              }
            },
            {
              "recordId": "3f1e25d1-37fa-4d77-804c-a865adebfe99",
              "readableId": "ENTITY-2"
            }
          ]
        },
        "counterpartyName": {
          "type": "string",
          "value": "Example Corporation"
        }
      }
    }
  ]
}

Endpoint: GET /public/api/v1/records/:id

Required OAuth Scope: public.records.readRecords

Query Parameters:

ParameterTypeRequiredDefaultDescription
hydrateEntitiesbooleanNofalseInclude full entity details in the response

6. Creating Records with Entity References

Endpoint: POST /records

Required OAuth Scope: public.records.createRecords

To create a record with a single entity, send the singular entity_reference. To create a record with multiple entities, send the plural entity_references array instead — see Multi-Entity Support for precedence rules if you send both.

Request Body (single entity):

{
  "type": "contract",
  "name": "My API Generated Record",
  "addProperties": {
    "entity_reference": {
      "type": "reference",
      "value": {
        "recordId": "ENTITY-1"
      }
    },
    "counterpartyName": {
      "type": "string",
      "value": "Example Company"
    },
    "agreementDate": {
      "type": "date",
      "value": "2024-01-15"
    }
  }
}

Request Body (multiple entities):

{
  "type": "contract",
  "name": "My API Generated Record",
  "addProperties": {
    "entity_references": {
      "type": "array",
      "value": [
        { "recordId": "ENTITY-1" },
        { "recordId": "ENTITY-2" }
      ]
    },
    "counterpartyName": {
      "type": "string",
      "value": "Example Company"
    },
    "agreementDate": {
      "type": "date",
      "value": "2024-01-15"
    }
  }
}

Example Request:

curl --request POST \
     --url 'https://na1.ironcladapp.com/public/api/v1/records' \
     --header 'Authorization: Bearer YOUR_ACCESS_TOKEN' \
     --header 'Content-Type: application/json' \
     --data '{
       "type": "contract",
       "name": "Sample Contract Record",
       "addProperties": {
         "entity_references": {
           "type": "array",
           "value": [
             { "recordId": "ENTITY-1" },
             { "recordId": "ENTITY-2" }
           ]
         },
         "counterpartyName": {
           "type": "string",
           "value": "Example Corporation"
         },
         "agreementDate": {
           "type": "date",
           "value": "2024-01-15"
         }
       }
     }'

Response Code: 201 Created

7. Updating Records with Entity References

Endpoint: PATCH /public/api/v1/records/:id

Required OAuth Scope: public.records.updateRecords

Important: if this request sends only the singular entity_reference, the write replaces the full entity list with that one entity — any additional entities added through the Ironclad UI since your last sync will be overwritten. To update a record without losing other entities, GET the record first, modify the full entity_references array, then PATCH with the plural field. If both fields are present in the same payload, entity_references wins and entity_reference is ignored, which makes this GET → modify → PATCH round trip safe without needing to strip the legacy field.

Example Request (safe round trip, plural field):

curl --request PATCH \
     --url 'https://na1.ironcladapp.com/public/api/v1/records/:id' \
     --header 'Authorization: Bearer YOUR_ACCESS_TOKEN' \
     --header 'Content-Type: application/json' \
     --data '{
       "addProperties": {
         "entity_references": {
           "type": "array",
           "value": [
             { "recordId": "ENTITY-1" },
             { "recordId": "ENTITY-2" }
           ]
         }
       }
     }'

Example Request (single entity, legacy field):

curl --request PATCH \
     --url 'https://na1.ironcladapp.com/public/api/v1/records/:id' \
     --header 'Authorization: Bearer YOUR_ACCESS_TOKEN' \
     --header 'Content-Type: application/json' \
     --data '{
       "addProperties": {
         "entity_reference": {
           "type": "reference",
           "value": {
             "recordId": "ENTITY-1"
           }
         }
       }
     }'

Response Code: 200 OK

Entity Reference Schema

In workflow schemas, entity reference fields appear as:

{
  "entity_reference": {
    "type": "referenceType",
    "displayName": "Counterparty",
    "propertyKey": "entity_reference"
  }
}

A workflow with multiple entity fields has one schema entry per entity field, each with its own propertyKey (its real attribute ID) — see the multi-entity schema example above.

Error Handling

When working with entity references, you may encounter the following specific errors:

Entity-Specific Error Codes

CodeDescription
INVALID_ENTITY_REFERENCEThe specified entity does not exist or is not accessible
FORBIDDENYou do not have permission to access the specified entity
INVALID_PARAMThe entity reference format is invalid or the entity is archived
NOT_FOUNDEntity with that reference could not be found

Error Response Format

{
  "code": "INVALID_ENTITY_REFERENCE",
  "message": "Entity with ID ENTITY-1 not found or not accessible",
  "param": "entity_reference"
}

Entity Reference Validation

When working with entity references, the following validation rules apply:

  1. Entity Existence: The referenced entity must exist in your Ironclad workspace
  2. Permission Requirements:
    • Read operations: The requesting user must have read access to the referenced entity
    • Write operations: The requesting user must have write access to both the workflow/record and the referenced entity
  3. Reference Format: Entity references can be provided as:
    • Entity Ironclad ID (e.g., ENTITY-1)
    • Entity record ID (UUID format)
  4. Attribute Type: When launching or updating a workflow, an entity attribute must be passed as a string (the entity's readable ID or record ID). Passing a non-string value (e.g., a number or object) now returns a validation error instead of failing silently.

Related Resources


Did this page help you?