API mutation and conflict resolution
Twilio allows multiple processes to update the same resource at the same time. In general, if two updates happen at the same time, the last one to complete will overwrite the previous one.
To help prevent accidental overwrites during concurrent updates, some Twilio API resources support optimistic concurrency control using the If-Match and ETag HTTP headers. Optimistic concurrency control allows multiple processes to attempt updates but requires each update to check if the resource has changed since it was last read. If the resource has changed, the update is rejected.
You can use the ETag and If-Match headers for the following resources:
Without the ETag and If-Match headers, the last update to a resource will overwrite any previous updates.
Create a Task with the attributes {"foo": "bar"}.
1// Download the helper library from https://www.twilio.com/docs/node/install2const twilio = require("twilio"); // Or, for ESM: import twilio from "twilio";34// Find your Account SID and Auth Token at twilio.com/console5// and set the environment variables. See http://twil.io/secure6const accountSid = process.env.TWILIO_ACCOUNT_SID;7const authToken = process.env.TWILIO_AUTH_TOKEN;8const client = twilio(accountSid, authToken);910async function createTask() {11const task = await client.taskrouter.v112.workspaces("WSXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX")13.tasks.create({ attributes: JSON.stringify({ foo: "bar" }) });1415console.log(task.accountSid);16}1718createTask();
Response
1{2"account_sid": "ACaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",3"age": 25200,4"assignment_status": "pending",5"attributes": "{\"foo\": \"bar\"}",6"date_created": "2014-05-14T18:50:02Z",7"date_updated": "2014-05-15T07:26:06Z",8"task_queue_entered_date": null,9"virtual_start_time": "2014-05-14T18:50:02Z",10"priority": 1,11"reason": "Test Reason",12"sid": "WTaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",13"task_queue_sid": "WQaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",14"task_channel_sid": "TCaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",15"task_channel_unique_name": "unique",16"timeout": 60,17"url": "https://taskrouter.twilio.com/v1/Workspaces/WSaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa/Tasks/WTaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",18"workspace_sid": "WSXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX",19"workflow_sid": "WWaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",20"workflow_friendly_name": "Example Workflow",21"task_queue_friendly_name": "Example Task Queue",22"ignore_capacity": false,23"routing_target": "WKaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",24"addons": "{}",25"links": {26"task_queue": "https://taskrouter.twilio.com/v1/Workspaces/WSaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa/TaskQueues/WQaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",27"workflow": "https://taskrouter.twilio.com/v1/Workspaces/WSaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa/Workflows/WWaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",28"workspace": "https://taskrouter.twilio.com/v1/Workspaces/WSaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",29"reservations": "https://taskrouter.twilio.com/v1/Workspaces/WSaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa/Tasks/WTaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa/Reservations"30}31}
Next, make two POST requests in quick succession to update the Task's attributes. This simulates concurrent updates from different sources.
POST A: Update"foo": "bar"to"foo": "bar-SFO".POST B: Update"foo": "bar"to"foo": "bar-DEN".
1// Download the helper library from https://www.twilio.com/docs/node/install2const twilio = require("twilio"); // Or, for ESM: import twilio from "twilio";34// Find your Account SID and Auth Token at twilio.com/console5// and set the environment variables. See http://twil.io/secure6const accountSid = process.env.TWILIO_ACCOUNT_SID;7const authToken = process.env.TWILIO_AUTH_TOKEN;8const client = twilio(accountSid, authToken);910async function updateTask() {11const task = await client.taskrouter.v112.workspaces("WSXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX")13.tasks("WTaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa")14.update({ attributes: JSON.stringify({ foo: "bar-SFO" }) });1516console.log(task.accountSid);17}1819updateTask();
Response
1{2"account_sid": "ACaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",3"age": 25200,4"assignment_status": "pending",5"attributes": "{\"foo\": \"bar-SFO\"}",6"date_created": "2014-05-14T18:50:02Z",7"date_updated": "2014-05-15T07:26:06Z",8"task_queue_entered_date": "2014-05-14T18:50:02Z",9"virtual_start_time": "2023-08-02T12:34:56Z",10"priority": 0,11"reason": "Test Reason",12"sid": "WTaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",13"task_queue_sid": "WQaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",14"task_channel_sid": "TCaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",15"task_channel_unique_name": "task-channel",16"timeout": 60,17"url": "https://taskrouter.twilio.com/v1/Workspaces/WSaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa/Tasks/WTaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",18"workflow_sid": "WWaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",19"workspace_sid": "WSXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX",20"workflow_friendly_name": "Test Workflow",21"task_queue_friendly_name": "Test Queue",22"addons": "{}",23"ignore_capacity": false,24"routing_target": "WKaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",25"links": {26"task_queue": "https://taskrouter.twilio.com/v1/Workspaces/WSaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa/TaskQueues/WQaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",27"workflow": "https://taskrouter.twilio.com/v1/Workspaces/WSaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa/Workflows/WWaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",28"workspace": "https://taskrouter.twilio.com/v1/Workspaces/WSaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",29"reservations": "https://taskrouter.twilio.com/v1/Workspaces/WSaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa/Tasks/WTaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa/Reservations"30}31}
1// Download the helper library from https://www.twilio.com/docs/node/install2const twilio = require("twilio"); // Or, for ESM: import twilio from "twilio";34// Find your Account SID and Auth Token at twilio.com/console5// and set the environment variables. See http://twil.io/secure6const accountSid = process.env.TWILIO_ACCOUNT_SID;7const authToken = process.env.TWILIO_AUTH_TOKEN;8const client = twilio(accountSid, authToken);910async function updateTask() {11const task = await client.taskrouter.v112.workspaces("WSXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX")13.tasks("WTaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa")14.update({ attributes: JSON.stringify({ foo: "bar-DEN" }) });1516console.log(task.accountSid);17}1819updateTask();
Response
1{2"account_sid": "ACaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",3"age": 25200,4"assignment_status": "pending",5"attributes": "{\"foo\": \"bar-DEN\"}",6"date_created": "2014-05-14T18:50:02Z",7"date_updated": "2014-05-15T07:26:06Z",8"task_queue_entered_date": "2014-05-14T18:50:02Z",9"virtual_start_time": "2023-08-02T12:34:56Z",10"priority": 0,11"reason": "Test Reason",12"sid": "WTaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",13"task_queue_sid": "WQaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",14"task_channel_sid": "TCaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",15"task_channel_unique_name": "task-channel",16"timeout": 60,17"url": "https://taskrouter.twilio.com/v1/Workspaces/WSaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa/Tasks/WTaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",18"workflow_sid": "WWaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",19"workspace_sid": "WSXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX",20"workflow_friendly_name": "Test Workflow",21"task_queue_friendly_name": "Test Queue",22"addons": "{}",23"ignore_capacity": false,24"routing_target": "WKaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",25"links": {26"task_queue": "https://taskrouter.twilio.com/v1/Workspaces/WSaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa/TaskQueues/WQaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",27"workflow": "https://taskrouter.twilio.com/v1/Workspaces/WSaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa/Workflows/WWaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",28"workspace": "https://taskrouter.twilio.com/v1/Workspaces/WSaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",29"reservations": "https://taskrouter.twilio.com/v1/Workspaces/WSaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa/Tasks/WTaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa/Reservations"30}31}
The POST B request overwrites the POST A request, and the Task's attributes are now "foo": "bar-DEN".
When you update a resource, the response returns a new ETag value that represents the current version of the resource. If you want to ensure that you're updating the most recent version, include the ETag value in the If-Match header of your update request.
- Make a
GETrequest to retrieve the Task with theETagheader. - Use the
ETagvalue in theIf-Matchheader when you make your update request.
If the ETag value and the If-Match value don't match, it means that the resource has been updated in-between and Twilio rejects your update with a 412 Precondition Failed error.
1// Download the helper library from https://www.twilio.com/docs/node/install2const twilio = require("twilio"); // Or, for ESM: import twilio from "twilio";34// Find your Account SID and Auth Token at twilio.com/console5// and set the environment variables. See http://twil.io/secure6const accountSid = process.env.TWILIO_ACCOUNT_SID;7const authToken = process.env.TWILIO_AUTH_TOKEN;8const client = twilio(accountSid, authToken);910async function updateTask() {11const task = await client.taskrouter.v112.workspaces("WSXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX")13.tasks("WTaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa")14.update({15attributes: JSON.stringify({ foo: "bar-DEN" }),16ifMatch: "0",17});1819console.log(task.accountSid);20}2122updateTask();
Response
1{2"account_sid": "ACaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",3"age": 25200,4"assignment_status": "pending",5"attributes": "{\"foo\": \"bar-DEN\"}",6"date_created": "2014-05-14T18:50:02Z",7"date_updated": "2014-05-15T07:26:06Z",8"task_queue_entered_date": "2014-05-14T18:50:02Z",9"virtual_start_time": "2023-08-02T12:34:56Z",10"priority": 0,11"reason": "Test Reason",12"sid": "WTaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",13"task_queue_sid": "WQaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",14"task_channel_sid": "TCaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",15"task_channel_unique_name": "task-channel",16"timeout": 60,17"url": "https://taskrouter.twilio.com/v1/Workspaces/WSaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa/Tasks/WTaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",18"workflow_sid": "WWaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",19"workspace_sid": "WSXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX",20"workflow_friendly_name": "Test Workflow",21"task_queue_friendly_name": "Test Queue",22"addons": "{}",23"ignore_capacity": false,24"routing_target": "WKaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",25"links": {26"task_queue": "https://taskrouter.twilio.com/v1/Workspaces/WSaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa/TaskQueues/WQaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",27"workflow": "https://taskrouter.twilio.com/v1/Workspaces/WSaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa/Workflows/WWaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",28"workspace": "https://taskrouter.twilio.com/v1/Workspaces/WSaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",29"reservations": "https://taskrouter.twilio.com/v1/Workspaces/WSaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa/Tasks/WTaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa/Reservations"30}31}
This operation fails with a 412 Precondition Failed response because the ETag (1) and the If-Match (0) values don't match.