Evolution of Update Capabilities

The requestedAction mechanism and the full patch-style update logic are already available in the current version of the API.

However, support for the replaceAll: true flag is part of an upcoming release.
This documentation anticipates that change to help you understand the evolution of the API and prepare for its extended update capabilities.

When it comes to updating records in GraphQL, particularly using Hot Chocolate, the logic closely resembles the concept of the method in RESTful APIs.

Remember

In GraphQL, the act of modification, including updates, aligns with the concept of a mutation.
Mutations in GraphQL are carried out using the HTTP method, which is consistent with all other mutation and query actions. This ensures a standardized and intuitive approach to making changes to data within the GraphQL schema.

Granular Data Modification

The focus is on modifying only the specific fields that need to be changed, rather than sending the entire dataset.
This approach ensures efficiency and minimizes unnecessary data transfer.

In a typical update, you only need to send the fields that have changed.
This patch-style logic avoids sending unchanged fields, reducing payload size and improving performance.
For example, if you want to update just the vatNumber of a customer, your mutation can be minimal and efficient:

mutation ($values: CustomerUpdateGLDtoInput!) {  
  updateCustomer (input: $values) {
    id
  }
}
{
  "values": {
    "id": "{currentId}",
    "vatNumber": "FR11123456789"
  }
}

However, when dealing with nested sub-resources, such as a customer’s contacts, or the phones belonging to those contacts, the situation becomes more complex.

These nested structures are common in various parts of the API, for example:

Resource Type Nested Sub-Resources
Customers contacts, phones, emails, socialMedias, addresses, paymentTermLines
Suppliers contacts, phones, emails, socialMedias, addresses, paymentTermLines
Employees contacts, phones, emails, socialMedias
SalesQuotes salesQuoteLines
SalesOrders salesOrderLines
SalesDeliveryNotes salesDeliveryNoteLines
SalesInvoices salesInvoiceLines
(and other similar hierarchical structures)

In the context of Sage Active Public API V2’s public API, the requestedAction field and the replaceAll flag play a crucial role when updating sub-resources, enabling precise control over whether to modify individual items or replace entire collections.

When dealing with nested sub-resources like contacts or phones, you have two possible strategies:

This hybrid model — patch by default, with optional replace by reset — allows flexibility depending on what the calling system is capable of providing.

For example, consider the case of updating a customer who has multiple contacts, and each contact has multiple phones.
In a single update operation, you might need to:

All these changes can be expressed together using the requestedAction field or simply relying on the presence or absence of id, as part of a patch-style mutation.

However, if your system does not track precisely what changed, for example, it only knows the final expected list of contacts and phones, then replaceAll becomes a better fit.
In that case, it’s easier to clear the previous values and recreate the full structure in one step. You can apply replaceAll at the appropriate collection level depending on whether you want to reset contacts, phones, or both.

Remember
  • Use requestedAction when your system can identify and send only the specific changes.
  • Use replaceAll when your system provides snapshots of the full target state, and you want to reset everything without computing diffs.

Example using requestedAction

This example demonstrates a patch-style update where:

{
  "id": "{customerId}",
  "contacts": [
    {
      // Modify existing contact's phones
      "id": "{contact1Id}",
      "requestedAction": "MODIFY", // Optional: can be omitted if id is present
      "phones": [
        {
          // Modify phone
          "id": "{phone1Id}",
          "requestedAction": "MODIFY",
          "number": "01 23 45 67 89"
        },
        {
          // Delete phone
          "id": "{phone2Id}",
          "requestedAction": "DELETE" // Required to delete
        },
        {
          // Create new phone
          "requestedAction": "CREATE", // Optional: can be omitted if id is absent
          "number": "06 07 08 09 10",
          "type": "MOBILE"
        }
      ]
    },
    {
      // Delete contact
      "id": "{contact2Id}",
      "requestedAction": "DELETE" 
    },
    {
      // Create new contact
      "requestedAction": "CREATE", 
      "name": "New Contact",
      "phones": [
        {
          "requestedAction": "CREATE", 
          "number": "05 55 55 55 55",
          "type": "LANDLINE"
        }
      ]
    }
  ]
}

Example using replaceAll

This example replaces the entire list of contacts and their phones:

{
  "id": "{customerId}",
  "contacts": {
    "replaceAll": true,
    "items": [
      {
        // Recreate contact "Alice" with new phones
        "name": "Alice",
        "phones": {
          "items": [
            {
              "number": "06 07 08 09 10",
              "type": "MOBILE"
            }
          ]
        }
      },
      {
        // Recreate contact "Bob" with new phones
        "name": "Bob",
        "phones": {
          "items": [
            {
              "number": "01 23 45 67 89",
              "type": "LANDLINE"
            }
          ]
        }
      }
    ]
  }
}

Summary Table: Sub-Resource Update Strategies

Scenario id present requestedAction Behavior
New item ❌ or CREATE Create
Modify existing item ❌ or MODIFY Update
Delete existing item DELETE (required) Delete
Recreate everything (replaceAll) ❌ / ✅ Ignored Full replacement

requestedAction: Rules and Requirements

This approach is suitable when the system replicating the data knows the exact changes to apply and can identify each sub-resource by its id.
Typically, this means the original application has stored the identifiers and is able to send only what was modified, added, or deleted.

When is requestedAction necessary?

This makes payloads lighter and simplifies integration for clients who do not wish to over-specify intent.

Example: Updating a contact’s phones using requestedAction

Effect:

{
  "id": "{customerId}",
  "contacts": [
    {
      "id": "{contactId}",
      "phones": [
        {
          "id": "{phone1Id}",
          "number": "01 23 45 67 89"
        },
        {
          "id": "{phone2Id}",
          "requestedAction": "DELETE"
        },
        {
          "number": "06 07 08 09 10",
          "type": "MOBILE"
        }
      ]
    }
  ]
}

replaceAll: Rules and Requirements

This approach is ideal when the system replicating the data does not track individual changes or identifiers, but instead only knows the final expected state.
For example, it may know that the customer has changed, but cannot determine what changed in the list of contacts or phones. In this case, it’s easier and safer to fully replace the collection.

The replaceAll flag can be used at any level of a nested resource, depending on the desired behavior.

In this second case, replaceAll is only required at the top level (e.g. contacts), and you do not need to repeat it for each sub-resource like phones, emails, or socialMedias.

Rules to keep in mind:

Example: Replacing all contacts and their phones

Effect:

{
  "id": "{customerId}",
  "contacts": {
    "replaceAll": true,
    "items": [
      {
        "name": "Alice",
        "phones": [
          {
            "number": "06 07 08 09 10",
            "type": "MOBILE"
          }
        ]
      },
      {
        "name": "Bob",
        "phones": [
          {
            "number": "01 23 45 67 89",
            "type": "LANDLINE"
          }
        ]
      }
    ]
  }
}

Example: Replacing only the phones of a specific contact

Effect:

This is useful when the external system knows the IDs of the contacts it needs to update, but does not track changes inside their sub-resources like phones.
It simply knows the final expected list of phones and prefers to overwrite the entire list.

{
  "id": "{customerId}",
  "contacts": {
    "items": [
      {
        "id": "{contactId1}",
        "name": "Alice",
        "phones": {
          "replaceAll": true,
          "items": [
            {
              "number": "06 99 88 77 66",
              "type": "MOBILE"
            },
            {
              "number": "01 11 22 33 44",
              "type": "LANDLINE"
            }
          ]
        }
      }
    ]
  }
}