Delete shopper information

In addition to consent requirements, the GDPR also ensures a shopper the right to erasure.

The right to erasure requires you to delete all data about a shopper on your sites if the shopper requests it. To support this right, Commerce provides endpoints in the Agent and Admin API that enable removal of personal data for consumer-based commerce shoppers and account-based commerce contacts. Using these endpoints, you can do the following:

  • Redact orders. Personal information about the shopper in a specified order is removed from the order and replaced with new data that does not identify the shopper. The order itself is retained in the system for reporting purposes.
  • Delete orders. The order and its constituent objects (such as line items, shipping groups, and payment groups) are removed from the server entirely and cannot be recovered. Note that only internal users who have the Administrator role can use the order deletion endpoints.
  • Delete or redact other objects that may contain personal data, such as purchase lists.
  • Delete profiles of registered shoppers or contacts. The shopper’s profile is deleted and personal data is discarded. For a registered shopper or contact, it is recommended that you delete or redact orders and other objects that contain personal data before deleting the shopper’s profile.
  • Delete notification requests for shopper-initiated email notifications for back in stock products, using either notification request ID, or using a profile ID/email ID.

Note that redaction and deletion are supported for orders associated with guest shoppers as well as registered shoppers.

This section describes how to use the REST APIs to delete shopper and contact information from your sites. It includes the following topics:

Important: Consult legal counsel for professional guidance about maintaining compliance with the GDPR right to erasure.

Redact orders

Commerce provides endpoints in the Agent API for redacting orders.

These endpoints overwrite shopper information in orders and replace it with automatically generated data.

To redact an order, you need its order ID. To find orders for a specific registered shopper, use the getOrders endpoint in the Admin API or the searchOrders endpoint in the Agent API. (The Admin API getOrders endpoint searches all orders, including orders needing approval, order quotes, and scheduled orders.) For example:

GET /ccadmin/v1/orders?queryFormat=SCIM&q=profileId eq "110658"  HTTP/1.1
Authorization: Bearer <access_token>
Content-Type: application/json

The response returns the orders associated with the specified profile ID. You can use these results to find the IDs of the orders you want to redact.

For some shoppers, you may not have a profile ID. This would be the case if you already deleted the shopper’s profile, or if the shopper never registered and instead checked out as a guest. If so, you can find the shopper’s orders by using the q query parameter to search for orders that contain a specific property value. For example:

GET /ccadmin/v1/orders?queryFormat=SCIM&q=shippingGroups.lastName eq "Brady"  HTTP/1.1
Authorization: Bearer <access_token>
Content-Type: application/json

To redact an individual order, you use the redactOrder endpoint in the Agent API, and include the order ID as a path parameter in the URL of the request. You specify the properties to redact in the properties array in the body of the request. For example:

POST /ccagent/v1/orders/o10042/redact  HTTP/1.1
Authorization: Bearer <access_token>
Content-Type: application/json

{
"properties": [
   "shippingGroups.lastName",
   "shippingGroups.city"
   ]
}

You can redact multiple orders in one request using the redactOrders endpoint in the Agent API. You specify the orders to redact in the orderIds array in the body of the request, and the properties to redact in the properties array. For example:

POST /ccagent/v1/orders/redact  HTTP/1.1
Authorization: Bearer <access_token>
Content-Type: application/json

{
  "orderIds": [
    "o10007",
    "o10042",
    "o10312",
    "o10842"
  ],
"properties": [
   "shippingGroups.lastName",
   "shippingGroups.city"
   ]
}

Redactable and non-redactable properties

When you redact an order, you should remove all personal data. (Consult legal counsel to determine which data should be considered personal.) Other data, however, should remain intact, so the order can be used in reporting. For example, the items purchased and the cost of the order are useful for tracking sales, and can be retained once they are no longer associated with a specific shopper.

To protect shopper privacy without discarding key data, Commerce designates which properties can be redacted and which cannot. For a complete list of the non-redactable properties, see the List of non-redactable order properties. Any order property that does not appear in this list can be redacted, including any custom properties.

Note that if you attempt to redact a property that is not redactable, the call will fail, and none of the specified properties will be redacted. For example:

POST /ccagent/v1/orders/o10057/redact  HTTP/1.1
Authorization: Bearer <access_token>
Content-Type: application/json

{
"properties": [
   "shippingGroups.lastName",
   "shippingGroups.city",
   "shippingGroups.priceInfo.currencyCode"
   ]
}

The response indicates which of the specified properties are not redactable:

{
    "errorCode": "28403",
    "message": "The following properties are blocked from redaction, please
       remove from the set of properties to be redacted -
       [shippingGroups.priceInfo.currencyCode]",
    "status": "400"
}

Redacted values

When you redact an order property, the value used to overwrite the existing value depends on the data type. The following table lists the various data types and the values they are set to when redacted:

Type Redacted Value
Integer 0
Long 0
Double 0.0
Float 0.0
Boolean null
Date Jan 1 12:00:00 GMT 1970
Timestamp Thursday, January 1, 1970 12:00:00 AM
String Randomly generated string
Enumeration Cannot be redacted

There are some properties that are exceptions to the values in the table:

  • profileId – The profileId property for the first order you redact is set to redact100001, and for subsequent orders it is set to redact100002, redact100003, and so on. If you use the redactOrders endpoint to redact multiple orders in a single call, then all orders specified in the call that have the same profileId are given the same redacted value, as described in Understand pseudonymization and anonymization
  • creditCardNumber – The redacted value of the creditCardNumber property is always xxxxxxxxxxxx1111.
  • approverIds (account-based orders only) – The redacted value of the approverIds property is the empty string.

Also, note that if you redact a Boolean custom property that has a default value, the property is set to the default value rather than to null.

Understand pseudonymization and anonymization

During redaction, string property values are replaced with automatically generated random strings. There are some differences you should be aware of between how redaction is done within a single call and across multiple calls.

If you redact orders individually (either by using the redactOrder endpoint or by using redactOrders and specifying a single order ID in each call), then there is no carry over of redacted string values from one call to the next. For example, even if all of the orders to be redacted have the same value for the shippingGroups.email property, the value this property is set to during redaction will be different in each call (and therefore for each order). The orders are anonymized so that they have no apparent connection to each other. The drawback of anonymization is that information that may be useful for reporting (such as the number of orders associated with a specific email address) is lost.

If you use the redactOrders endpoint to redact multiple orders in a single call, however, the same string is used in all of these orders to redact the same value. So if the value of shippingGroups.email is the same in each order before redaction, the same random string will be used in each order to redact this property. Although the original value is replaced, the fact that all of the orders had the same value for the property will be preserved. In this case, the order is pseudonymized, indicating that a single random string is used as a pseudonym for the original value. The drawback of pseudonymization is that someone examining the orders may be able to draw inferences about the original values. Hence pseudonymization is somewhat less secure than anonymization. Keep in mind, too, that pseudonymization applies only to orders redacted in the same REST call. If you make a subsequent call to redact additional orders, the redacted values used in the first call are not carried over to the second.

Redact scheduled orders

A scheduled order consists of two parts: an order template that specifies the items to include in the orders that are created, and a scheduling object that determines when those orders are submitted. You can use the Scheduled Orders endpoints in the Agent API to delete the scheduling object for a scheduled order, to ensure that no further orders are created from the template. First, use the listScheduledOrdersByProfile endpoint to find the shopper or contact’s scheduled orders. You can then use the deleteScheduledOrders endpoint to delete the schedules for those orders.

To remove personal data in the template, you can redact the order template, just as you would any other order. However, if you want to redact orders that have been created from the template, you must locate them and specify them separately.

For more information about scheduled orders, see Create Scheduled Orders. For more information about the Scheduled Orders endpoints in the Agent API, see the REST API documentation in the Oracle Help Center.

Order Redact webhook

Oracle Commerce includes an Order Redact event webhook. You can configure this webhook to notify external systems when an order is redacted. The webhook payload contains the order ID of the redacted order. For example:

{
  "orderId": "o30411"
}

See the Use Webhooks chapter for information about configuring and using webhooks.

Delete orders

Internal users who have the Administrator access role can use REST endpoints to delete orders. When an order is deleted, the order and its constituent objects (such as line items, shipping groups, and payment groups) are removed from the server entirely, so any reports you generate will not take into account data from the order. If you want to remove personal information from orders while still retaining other data for reports, you can redact the orders instead.

To delete an order, you need its order ID. To find orders for a specific registered shopper, use the getOrders endpoint in the Admin API or the searchOrders endpoint in the Agent API, as described in Redact orders.

To delete an individual order, you use the deleteOrder endpoint in the Admin API, and include the order ID as a path parameter in the URL of the request. For example:

DELETE /ccadmin/v1/orders/o10042  HTTP/1.1
Authorization: Bearer <access_token>
Content-Type: application/json

You can delete multiple orders in one request using the deleteOrders endpoint in the Admin API. You specify the orders to delete in the orderIds array in the body of the request. For example:

POST /ccadmin/v1/orders/delete  HTTP/1.1
Authorization: Bearer <access_token>
Content-Type: application/json

{
  "orderIds": [
    "o10007",
    "o10042",
    "o10312",
    "o10842"
  ]
}

Delete scheduled orders

A scheduled order consists of two parts: an order template that specifies the items to include in the orders that are created, and a scheduling object that determines when those orders are submitted. When a new order is created from the template, the order’s createdByOrderId property contains a reference to the template. To delete a scheduled order, use the deleteOrder or deleteOrders endpoints to delete the order template. When you delete an order template, the associated scheduling object is deleted as well. However, if you want to delete orders that have been created from the template, you must locate them and specify them separately.

Delete returns and exchanges

When a shopper returns an item, a return request object is created and associated with the order. If the shopper makes multiple returns against one order, each return creates a new return request object that is added to the original order.

When a shopper requests an exchange, a return request object and a new order are created. The return request contains a reference to the follow-up order. If the shopper subsequently requests a return or exchange for the follow-up order, a new return request object is created for that order. Return requests and exchange orders can be chained together without limit, and each return request can have its own chain of exchanges and further returns.

When you delete an order, any return request objects belonging to the order are also deleted. But if a return request object has a follow-up (exchange) order associated with it, that order is not deleted when the original order is deleted. You must locate any exchange orders and delete them separately.

Delete quotes

When a shopper requests a quote, a quoteInfo object is created and associated with the order, and the order state changes to PENDING_QUOTE. When the quote is received, the original order and its quoteInfo object are copied to create a new order. (The copy’s order state is thus PENDING_QUOTE, and its quoteInfo reflects the original quoteInfo.) The state of the original order is then changed to QUOTED, and the original order and quoteInfo object are updated to reflect the quoted values.

Each time the shopper requests a requote, a new QUOTED order and quoteInfo are created. The result is that there is one order for every quote, plus a single order whose state is PENDING_QUOTE. Each of these orders has at most one quoteInfo.

When a quoted order is deleted, its associated quoteInfo is also deleted. Other orders created through the quoting process are not deleted automatically. You must locate these orders and delete them separately.

Order Delete webhook

Oracle Commerce includes an Order Delete event webhook. You can configure this webhook to notify external systems when an order is deleted. The webhook payload contains the order ID of the deleted order. For example:

{
  "orderId": "o30419"
}

See the Use Webhooks chapter for information about configuring and using webhooks.

Delete shopper profiles

Commerce provides endpoints in the Agent API for deleting shopper profiles. These endpoints delete profiles securely to ensure they can no longer be accessed. Note that once a profile is deleted, it cannot be accessed on any site running on your Commerce instance.

Important: Before you delete a shopper’s profile, you should first redact or delete all of that shopper’s orders. Deleting a profile does not automatically remove personal data from orders associated with it.

To delete a profile, you need its profile ID. You can find the profile by using the searchProfiles endpoint in the Agent API. Use the q query parameter to search for profiles that contain a specific property value. For example:

GET /ccagent/v1/profiles?queryFormat=SCIM&q=email eq "floeb@example.com"  HTTP/1.1
Authorization: Bearer <access_token>
Content-Type: application/json

The response includes the profile ID:

{
  . . .
    "items": [
        {
            "firstName": "Fred",
            "lastName": "Loeb",
            "profileType": null,
            "repositoryId": "110332",
            "shippingAddress": {
                "phoneNumber": "617-555-1212",
                "postalCode": "01012",
                "repositoryId": "130417"
            },
            "id": "110332",
            "email": "floeb@example.com",
        }
    ]
}

To delete a single profile, you use the deleteProfile endpoint in the Agent API. You specify the profile to delete by including the profile ID as a path parameter in the URL of the request. For example, to delete the profile shown above:

DELETE /ccagent/v1/profiles/110332  HTTP/1.1
Authorization: Bearer <access_token>
Content-Type: application/json

To delete multiple profiles with a single request, you use the deleteProfiles endpoint in the Agent API. You specify the profiles to delete in the profileIds array in the body of the request. For example:

DELETE /ccagent/v1/profiles  HTTP/1.1
Authorization: Bearer <access_token>
Content-Type: application/json

{
  "profileIds": [
    "110332",
    "150027",
    "160035"
  ]
}

Delete or redact purchase lists

You can use the Purchase List endpoints in the Agent API to delete or redact purchase lists associated with a specific shopper or contact. To find the purchase lists, use the getPurchaseList endpoint. You can then use the deletePurchaseList endpoint to delete the shopper’s purchase lists individually, by providing the purchase list ID as a path parameter. As an alternative, you can use the updatePurchaseList endpoint to redact any personal data in the purchase list by overwriting it.

For more information about purchase lists, see the Enable Purchase Lists chapter. For more information about the Purchase List endpoints in the Agent API, see the REST API documentation in the Oracle Help Center.

Delete back in stock notification requests

When you delete a shopper profile, you may also want to delete any email notification requests associated with the shopper for back in stock products. Commerce provides two endpoints in the Admin API for this purpose.

The deleteProductNotification endpoint deletes a single notification request whose ID is specified as a path parameter. For example:

DELETE /ccadmin/v1/productnotify/330007  HTTP/1.1
Authorization: Bearer <access_token>
Content-Type: application/json

The deleteProductNotificationByProfileIdOrEmail endpoint deletes all of the notification requests that match a specific email address or profile ID. The email address can be specified using the email query parameter, or the profile ID can be specified using the profileId query parameter. For example, to delete all of the notification requests associated with a specific profile ID:

DELETE /ccadmin/v1/productnotify?profileId=160035  HTTP/1.1
Authorization: Bearer <access_token>
Content-Type: application/json

For more information about these endpoints, see the REST API documentation in the Oracle Help Center.

Shopper Profile Delete webhook

Oracle Commerce includes a Shopper Profile Delete event webhook. You can configure this webhook to notify external systems when a profile is deleted. The webhook payload contains the profile ID of the deleted profile. For example:

{
  "profileId": "110658"
}

See the Use Webhooks chapter for information about configuring and using webhooks.

Delete contact data for account-based commerce

If any of your sites support account-based commerce, you have shoppers called contacts who are associated with specific accounts. You can delete the profiles for contacts just as you would for consumer-based commerce shoppers. You can also redact or delete orders associated with contacts.

However, there are some additional considerations you must be aware of when you delete contacts and redact or delete data associated with their orders. This section discusses other steps you should perform and additional data you should delete or redact. It includes the following topics:

  • Delete a contact
  • Delete an approver
  • Redact account and contact registration requests

Note that if your sites store personal data in other account-based objects such as organizations, you can also use endpoints in the REST APIs to overwrite this data.

For more information about account-based commerce, see Configure Business Accounts.

Delete a contact

Before deleting a contact’s profile, there are a few related actions you should perform:

  • Redact or delete any orders associated with the contact. Note that account-based orders have a few additional properties (approverMessages, approvalSystemMessages, approverIds, and organizationId) not found in consumer-based orders. These additional properties are all redactable.
  • Use the administration interface or Admin API to redact any registration requests associated with the contact. See Redact registration requests.
  • Redact or delete the contact’s scheduled orders.
  • Manually reject any orders submitted by the contact that are still pending approval.
  • Redact or delete the contact’s purchase lists.

Delete an approver

A contact’s profile cannot be deleted if the contact has the Approver role for any account. You must first remove the Approver role from the contact for all accounts before deleting the profile. You can do this in the administration interface or by using the Admin API. Note that if there is only one approver for an account, you cannot remove the Approver role from that contact until you add another approver to the account.

Before you delete an approver’s profile, you should remove the approver’s ID from orders. To find the orders that have been approved by this approver, use the getOrders endpoint in the Admin API. For example:

GET /ccadmin/v1/orders?queryFormat=SCIM&q=approverIds co "bb-110031"  HTTP/1.1
Authorization: Bearer <access_token>
Content-Type: application/json

Once you have the list of orders, use the redactOrder or redactOrders endpoint to redact the approverIds property. For example, to redact the approverIds property on a single order:

POST /ccagent/v1/orders/o10051/redact  HTTP/1.1
Authorization: Bearer <access_token>
Content-Type: application/json

{
"properties": [
   "approverIds"
   ]
}

When you redact an order’s approverIds property, its value is set to the empty string.

Redact registration requests

If a contact has submitted any registration requests, you should redact these requests before deleting the contact’s profile. Note that you should reject or approve any pending registration requests before redacting them or deleting the contact. If you delete a contact that has any pending requests, you will only be able to reject those requests afterward.

You can use the Organization Requests endpoints in the Admin API to redact a contact’s registration requests. Use the listOrganizationRequests endpoint to find the relevant requests, and then use the updateOrganizationRequests endpoint to overwrite the fields you want to redact.

List of non-redactable order properties

The following is a list of all of the order properties whose values cannot be redacted:

id 
state 
creationDate 
createdByOrderId 
submittedDate 
lastModifiedDate 
completedDate 
priceInfo 
taxPriceInfo 
siteId 
locale 
priceGroupId 
taxExempt 
taxCalculated 
externalOrderPriceDetails 
recurringChargePriceInfo 
secondaryCurrencyCode 
exchangeRate 
catalogId 
commerceItems.commerceItemId 
commerceItems.catalogId 
commerceItems.catalogRefId 
commerceItems.catalogKey 
commerceItems.productId 
commerceItems.siteId 
commerceItems.quantity 
commerceItems.state 
commerceItems.stateDetail 
commerceItems.productTaxCode 
commerceItems.externalPriceDetails 
commerceItems.quantityWithFraction 
commerceItems.recurringChargePriceInfo 
commerceItems.externalRecurringChargeDetails 
priceInfo.rawSubTotal 
priceInfo.tax 
priceInfo.shipping 
priceInfo.manualAdjustmentTotal 
priceInfo.type 
priceInfo.currencyCode 
priceInfo.amount 
priceInfo.discounted 
priceInfo.amountIsFinal 
priceInfo.finalReasonCode 
priceInfo.adjustments.adjustmentDescription 
priceInfo.adjustments.pricingModel 
priceInfo.adjustments.manualPricingAdjustment 
priceInfo.adjustments.coupon 
priceInfo.adjustments.totalAdjustment 
priceInfo.adjustments.quantityAdjusted 
priceInfo.adjustments.quantityWithFractionAdjusted 
commerceItems.priceInfo.listPrice 
commerceItems.priceInfo.rawTotalPrice 
commerceItems.priceInfo.salePrice 
commerceItems.priceInfo.onSale 
commerceItems.priceInfo.orderDiscountShare 
commerceItems.priceInfo.quantityDiscounted 
commerceItems.priceInfo.quantityAsQualifier 
commerceItems.priceInfo.priceList 
commerceItems.priceInfo.discountable 
commerceItems.priceInfo.shippingSurcharge 
commerceItems.priceInfo.quantityWithFractionAsQualifier 
commerceItems.priceInfo.quantityWithFractionDiscounted 
commerceItems.priceInfo.currentPriceDetails 
commerceItems.priceInfo.type 
commerceItems.priceInfo.currencyCode 
commerceItems.priceInfo.amount 
commerceItems.priceInfo.discounted 
commerceItems.priceInfo.amountIsFinal 
commerceItems.priceInfo.finalReasonCode 
commerceItems.priceInfo.adjustments.adjustmentDescription 
commerceItems.priceInfo.adjustments.pricingModel 
commerceItems.priceInfo.adjustments.manualPricingAdjustment 
commerceItems.priceInfo.adjustments.coupon 
commerceItems.priceInfo.adjustments.adjustments.totalAdjustment 
commerceItems.priceInfo.adjustments.quantityAdjusted 
commerceItems.priceInfo.adjustments.quantityWithFractionAdjusted 
taxPriceInfo.cityTax 
taxPriceInfo.countyTax 
taxPriceInfo.stateTax 
taxPriceInfo.countryTax 
taxPriceInfo.valueAddedTax 
taxPriceInfo.miscTax 
taxPriceInfo.isTaxIncluded 
taxPriceInfo.secondaryCurrencyTaxAmount 
taxPriceInfo.type 
taxPriceInfo.currencyCode 
taxPriceInfo.amount 
taxPriceInfo.discounted 
taxPriceInfo.amountIsFinal 
taxPriceInfo.finalReasonCode 
taxPriceInfo.adjustments.adjustmentDescription 
taxPriceInfo.adjustments.pricingModel 
taxPriceInfo.adjustments.manualPricingAdjustment 
taxPriceInfo.adjustments.coupon 
taxPriceInfo.adjustments.totalAdjustment 
taxPriceInfo.adjustments.quantityAdjusted 
taxPriceInfo.adjustments.quantityWithFractionAdjusted 
shippingGroups.priceInfo.rawShipping 
shippingGroups.priceInfo.shippingTax 
shippingGroups.priceInfo.secondaryCurrencyTaxAmount 
shippingGroups.priceInfo.type 
shippingGroups.priceInfo.currencyCode 
shippingGroups.priceInfo.amount 
shippingGroups.priceInfo.discounted 
shippingGroups.priceInfo.amountIsFinal 
shippingGroups.priceInfo.finalReasonCode 
shippingGroups.priceInfo.adjustments.adjustmentDescription 
shippingGroups.priceInfo.adjustments.pricingModel 
shippingGroups.priceInfo.adjustments.manualPricingAdjustment 
shippingGroups.priceInfo.adjustments.coupon 
shippingGroups.priceInfo.adjustments.totalAdjustment 
shippingGroups.priceInfo.adjustments.quantityAdjusted 
shippingGroups.priceInfo.adjustments.quantityWithFractionAdjusted 
commerceItems.currentPriceDetails.tax 
commerceItems.currentPriceDetails.orderDiscountShare 
commerceItems.currentPriceDetails.orderManualAdjustmentShare 
commerceItems.currentPriceDetails.quantityAsQualifier 
commerceItems.currentPriceDetails.quantityWithFractionAsQualifier 
commerceItems.currentPriceDetails.quantityWithFraction 
commerceItems.currentPriceDetails.secondaryCurrencyTaxAmount 
commerceItems.currentPriceDetails.type 
commerceItems.currentPriceDetails.currencyCode 
commerceItems.currentPriceDetails.amount 
commerceItems.currentPriceDetails.discounted 
commerceItems.currentPriceDetails.amountIsFinal 
commerceItems.currentPriceDetails.finalReasonCode 
commerceItems.currentPriceDetails.adjustments.adjustmentDescription 
commerceItems.currentPriceDetails.adjustments.pricingModel 
commerceItems.currentPriceDetails.adjustments.manualPricingAdjustment 
commerceItems.currentPriceDetails.adjustments.coupon 
commerceItems.currentPriceDetails.adjustments.totalAdjustment 
commerceItems.currentPriceDetails.adjustments.quantityAdjusted 
commerceItems.currentPriceDetails.adjustments.quantityWithFractionAdjusted 
paymentGroups.paymentGroupClassType 
paymentGroups.paymentMethod 
paymentGroups.amount 
paymentGroups.amountAuthorized 
paymentGroups.amountDebited 
paymentGroups.amountCredited 
paymentGroups.currencyCode 
paymentGroups.state 
paymentGroups.submittedDate 
paymentGroups.cancelledDate 
shippingGroups.shippingGroupClassType 
shippingGroups.shippingMethod 
shippingGroups.state 
shippingGroups.submittedState 
relationships.shippingGroup 
relationships.commerceItem 
relationships.quantity 
relationships.returnedQuantity 
relationships.amount 
relationships.state 
relationships.quantityWithFraction 
relationships.returnedQuantityWithFraction 
externalPriceDetails.externalPrice 
externalPriceDetails.externalPriceQuantity