Relationships and included resources
A relationship explains how one resource connects to another. The relationship name and the related resource type can differ: contactAddress is a relationship name, while contact-address is a resource type.
Three ways to ask
| Request | What you are asking for |
|---|---|
GET /jsonapi/contacts/1001 | The contact |
GET /jsonapi/contacts/1001/contactAddress | The related address resources |
GET /jsonapi/contacts/1001/relationships/contactAddress | The address identifiers in the relationship |
Use the related-resource endpoint when you need address attributes. Use the relationship endpoint when identifiers are enough. The available operations and access rules vary by relationship.
A relationship's links.self points to the linkage endpoint; its links.related points to the related resource endpoint. The meaning of self depends on where it appears: the contact resource's own links.self points to the contact.
A to-one relationship holds one identifier or null. A to-many relationship holds an array, even when there is only one related record. Use the declared cardinality rather than changing your parser according to the number of records returned.
Include details in one request
GET /jsonapi/contacts/1001?include=contactAddress
The contact remains in data; the addresses appear in a top-level included array:
{
"data": {
"type": "contacts",
"id": "1001",
"attributes": { "firstName": "Alex" },
"relationships": {
"contactAddress": {
"data": [{ "type": "contact-address", "id": "2001" }]
}
}
},
"included": [
{
"type": "contact-address",
"id": "2001",
"attributes": {
"addressType": "PostalAddress",
"formattedAddress": "PO Box 100, Brisbane QLD 4000"
}
}
]
}
To resolve a relationship, index included by (type, id) and look up the corresponding identifier. Do not join by array position. The same resource can be referenced by multiple records and need only appear once in the document.
Resolve included records by identity
For the single-contact example, this JavaScript builds a lookup for the document and resolves the address identifiers:
const key = (resource) => JSON.stringify([resource.type, resource.id]);
const primary = Array.isArray(payload.data)
? payload.data
: payload.data
? [payload.data]
: [];
const resources = new Map(
[...primary, ...(payload.included ?? [])].map((resource) => [
key(resource),
resource,
]),
);
const contact = resources.get(JSON.stringify(["contacts", "1001"]));
const addressIds = contact?.relationships?.contactAddress?.data ?? [];
const addresses = addressIds.map((id) => resources.get(key(id)));
An unresolved lookup means the details are not in this document. It does not prove the related record was deleted. Fetch permitted related data if the workflow needs it. The lookup is only an illustration; production code should also validate the expected response shape.
Nested includes
Use commas for separate relationships and dots to traverse a supported relationship path. For example, include=contacts,contacts.contactAddress asks for contacts and their addresses where those relationships are supported by the primary resource.
The reviewed MyCRM configuration limits include depth to four. Deep includes and includes on collection searches can substantially increase request cost. Start from the resource closest to the information you need and request only useful relationships.
Included collections can be partial
A to-many include can be paginated or filtered. Do not assume every related record is present merely because the relationship was included. When you need a complete related collection, use its documented related-resource endpoint and page through it explicitly. Top-level pagination describes the primary collection, not every included collection.
Try the interactive example or continue to querying.