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"required_attention": 0,26"links": {27"task_queue": "https://taskrouter.twilio.com/v1/Workspaces/WSaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa/TaskQueues/WQaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",28"workflow": "https://taskrouter.twilio.com/v1/Workspaces/WSaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa/Workflows/WWaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",29"workspace": "https://taskrouter.twilio.com/v1/Workspaces/WSaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",30"reservations": "https://taskrouter.twilio.com/v1/Workspaces/WSaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa/Tasks/WTaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa/Reservations"31}32}
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"required_attention": 0,26"links": {27"task_queue": "https://taskrouter.twilio.com/v1/Workspaces/WSaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa/TaskQueues/WQaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",28"workflow": "https://taskrouter.twilio.com/v1/Workspaces/WSaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa/Workflows/WWaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",29"workspace": "https://taskrouter.twilio.com/v1/Workspaces/WSaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",30"reservations": "https://taskrouter.twilio.com/v1/Workspaces/WSaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa/Tasks/WTaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa/Reservations"31}32}
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"required_attention": 0,26"links": {27"task_queue": "https://taskrouter.twilio.com/v1/Workspaces/WSaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa/TaskQueues/WQaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",28"workflow": "https://taskrouter.twilio.com/v1/Workspaces/WSaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa/Workflows/WWaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",29"workspace": "https://taskrouter.twilio.com/v1/Workspaces/WSaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",30"reservations": "https://taskrouter.twilio.com/v1/Workspaces/WSaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa/Tasks/WTaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa/Reservations"31}32}
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
GET
request to retrieve the Task with theETag
header. - Use the
ETag
value in theIf-Match
header 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"required_attention": 0,26"links": {27"task_queue": "https://taskrouter.twilio.com/v1/Workspaces/WSaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa/TaskQueues/WQaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",28"workflow": "https://taskrouter.twilio.com/v1/Workspaces/WSaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa/Workflows/WWaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",29"workspace": "https://taskrouter.twilio.com/v1/Workspaces/WSaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",30"reservations": "https://taskrouter.twilio.com/v1/Workspaces/WSaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa/Tasks/WTaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa/Reservations"31}32}
This operation fails with a 412 Precondition Failed
response because the ETag
(1
) and the If-Match
(0
) values don't match.