---
"@context": https://schema.org
"@type": TechArticle
"@id": https://www.twilio.com/docs/taskrouter/quickstart/php/agent-ui-add-project#article
headline: "Control Worker Activities using Worker.js: Add an Agent UI to our Project"
description: Build a real-time agent UI for Twilio TaskRouter using TaskRouter.js and PHP, letting Workers change Activity state and receive Reservation event notifications.
url: https://www.twilio.com/docs/taskrouter/quickstart/php/agent-ui-add-project
inLanguage: en
dateModified: 2026-07-06T16:59:46.000Z
author:
  "@type": Organization
  name: Twilio Developer Education Team
publisher:
  "@type": Organization
  name: Twilio
---

# Control Worker Activities using Worker.js: Add an Agent UI to our Project

To build the agent UI, create a web page that initializes TaskRouter.js. Assuming you've followed the conventions so far in this tutorial, the UI you create will be accessible in your web browser at:

`http://localhost:8080/agent.php?WorkerSid=WKXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX` (substitute your Alice's WorkerSid)

We pass the WorkerSid in the URL to avoid implementing complex user management in our demo. In reality, you are likely to store a user's WorkerSid in your database alongside other User attributes.

First, create a file called `agent.php` and add the following code. Remember to substitute your own account and Workspace credentials between the curly braces:

```php title="agent.php"
<?php
// Get the Twilio-PHP library from twilio.com/docs/libraries/php,
// following the instructions to install it with Composer.
require_once "vendor/autoload.php";

// Your Account SID from www.twilio.com/console
// To set up environmental variables, see https://twil.io/secure
$accountSid = getenv('TWILIO_ACCOUNT_SID');
// Your Auth Token from www.twilio.com/console
$authToken = getenv('TWILIO_AUTH_TOKEN');
$workspaceSid = 'WSXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX';

$workerSid = $_REQUEST['WorkerSid'];

$workerCapability = new Twilio\Jwt\TaskRouter\WorkerCapability(
    $accountSid, $authToken, $workspaceSid, $workerSid);
$workerCapability->allowActivityUpdates();
$workerToken = $workerCapability->generateToken();

?>
<!DOCTYPE html>
<html>
<head>
    <title>Customer Care - Voice Agent Screen</title>
    <link rel="stylesheet" href="//media.twiliocdn.com/taskrouter/quickstart/agent.css"/>
    <script src="//media.twiliocdn.com/taskrouter/js/v1.13/taskrouter.min.js"></script>
    <script src="agent.js"></script>
</head>
<body>
<div class="content">
    <section class="agent-activity offline">
        <p class="activity">Offline</p>
        <button class="change-activity" data-next-activity="Idle">Go Available</button>
    </section>
    <section class="agent-activity idle">
        <p class="activity"><span>Available</span></p>
        <button class="change-activity" data-next-activity="Offline">Go Offline</button>
    </section>
    <section class="agent-activity reserved">
        <p class="activity">Reserved</p>
    </section>
    <section class="agent-activity busy">
        <p class="activity">Busy</p>
    </section>
    <section class="agent-activity wrapup">
        <p class="activity">Wrap-Up</p>
        <button class="change-activity" data-next-activity="Idle">Go Available</button>
        <button class="change-activity" data-next-activity="Offline">Go Offline</button>
    </section>
    <section class="log">
      <textarea id="log" readonly="true"></textarea>
    </section>
</div>
<script>
  window.workerToken = "<?= $workerToken ?>";
</script>
</body>
</html>
```

You'll notice that we included three external files:

* `taskrouter.min.js` is the primary TaskRouter.js JavaScript file that communicates with TaskRouter's infrastructure on your behalf. You can use this URL to include TaskRouter.js in your production application, but first check the [TaskRouter.js Worker reference documentation](/docs/taskrouter/js-sdk-v1/workspace/worker) to confirm the version number.
* `agent.css` is a CSS file created for this Quickstart. It provides some predefined styles.
* `agent.js` contains the custom JavaScript logic for this application. You'll create that next.

Download and extract [Twilio's PHP SDK](https://github.com/twilio/twilio-php) into the same directory that you are working in. This is required as our code references `twilio-php/Services/Twilio.php`. For more advanced PHP users, the composer package manager will also work fine. See the [twilio-php documentation](https://www.twilio.com/docs/libraries/reference/twilio-php/).

Create the agent.js file in the same directory as your agent.php and add the following code:

## agent.js

```javascript
/* Subscribe to a subset of the available Worker.js events */

function registerTaskRouterCallbacks() {
  worker.on('ready', function(worker) {
      agentActivityChanged(worker.activityName);
      logger("Successfully registered as: " + worker.friendlyName)
      logger("Current activity is: " + worker.activityName);
  });

  worker.on('activity.update', function(worker) {
      agentActivityChanged(worker.activityName);
      logger("Worker activity changed to: " + worker.activityName);
  });

  worker.on("reservation.created", function(reservation) {
    logger("-----");
    logger("You have been reserved to handle a call!");
    logger("Call from: " + reservation.task.attributes.from);
    logger("Selected language: " + reservation.task.attributes.selected_language);
    logger("-----");
  });

  worker.on("reservation.accepted", function(reservation) {
    logger("Reservation " + reservation.sid + " accepted!");
  });

  worker.on("reservation.rejected", function(reservation) {
    logger("Reservation " + reservation.sid + " rejected!");
  });

  worker.on("reservation.timeout", function(reservation) {
    logger("Reservation " + reservation.sid + " timed out!");
  });

  worker.on("reservation.canceled", function(reservation) {
    logger("Reservation " + reservation.sid + " canceled!");
  });
}

/* Hook up the agent Activity buttons to Worker.js */

function bindAgentActivityButtons() {
  // Fetch the full list of available Activities from TaskRouter. Store each
  // ActivitySid against the matching Friendly Name
  var activitySids = {};
  worker.activities.fetch(function(error, activityList) {
    var activities = activityList.data;
    var i = activities.length;
    while (i--) {
      activitySids[activities[i].friendlyName] = activities[i].sid;
    }
  });

  /* For each button of class 'change-activity' in our Agent UI, look up the
  ActivitySid corresponding to the Friendly Name in the button's next-activity
  data attribute. Use Worker.js to transition the agent to that ActivitySid
  when the button is clicked.*/
  var elements = document.getElementsByClassName('change-activity');
  var i = elements.length;
  while (i--) {
      elements[i].onclick = function() {
        var nextActivity = this.dataset.nextActivity;
        var nextActivitySid = activitySids[nextActivity];
        worker.update({"ActivitySid":nextActivitySid});
      }
  }
}

/* Update the UI to reflect a change in Activity */

function agentActivityChanged(activity) {
  hideAgentActivities();
  showAgentActivity(activity);
}

function hideAgentActivities() {
  var elements = document.getElementsByClassName('agent-activity');
  var i = elements.length;
  while (i--) {
      elements[i].style.display = 'none';
  }
}

function showAgentActivity(activity) {
  activity = activity.toLowerCase();
  var elements = document.getElementsByClassName(('agent-activity ' + activity));
  elements.item(0).style.display = 'block';
}

/* Other stuff */

function logger(message) {
  var log = document.getElementById('log');
  log.value += "\n> " + message;
  log.scrollTop = log.scrollHeight;
}

window.onload = function() {
  // Initialize TaskRouter.js on page load using window.workerToken -
  // a Twilio Capability token that was set in a <script> in agent.php
  logger("Initializing...");
  window.worker = new Twilio.TaskRouter.Worker(workerToken);
  registerTaskRouterCallbacks();
  bindAgentActivityButtons();
};
```

Open `http://localhost:8080/agent.php?WorkerSid=WK012340123401234` in your browser and you should see the screen below. If you make the same phone call as in Part 3, you should see Alice's Activity transition on screen as she is reserved and assigned to handle the Task.

If you see "Initializing..." and no progress, make sure that you have included the correct WorkerSid in the "WorkerSid" request parameter of the URL.

For more details, refer to the [TaskRouter.js Worker JavaScript SDK documentation](/docs/taskrouter/js-sdk-v1/workspace/worker).

## Completed Agent UI

The completed agent UI displays the Worker's current Activity and lets them change it. The image below shows the UI with Alice in the "Ready" state.

![Agent UI showing status 'Ready' with option to 'Go Offline'.](https://docs-resources.prod.twilio.com/ea5b8f6f3541c5a014adba0c1992eeb6b43939773cdf2ee8e44d8645c5055edb.png)
