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
– TheprofileId
property for the first order you redact is set toredact100001
, and for subsequent orders it is set toredact100002
,redact100003
, and so on. If you use theredactOrders
endpoint to redact multiple orders in a single call, then all orders specified in the call that have the sameprofileId
are given the same redacted value, as described in Understand pseudonymization and anonymizationcreditCardNumber
– The redacted value of thecreditCardNumber
property is alwaysxxxxxxxxxxxx1111
.approverIds
(account-based orders only) – The redacted value of theapproverIds
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
, andorganizationId
) 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