# WebDriver endpoint (Appium-style)

Open a WebDriver session on a device at /wd/hub, find elements, act, take screenshots and end the session, from an existing test client.

The engine serves a W3C WebDriver endpoint at `/wd/hub` in the Appium style, so a test client you already have can open a session on a device, find elements, act and take screenshots. Every command runs through the same engine, leases, safety rules and audit as REST and MCP. It is an Appium-style WebDriver endpoint, not a full Appium server. Status: **supported**. Proven on an iPhone 11 (iOS 27) on a cable: a session, a screenshot and the end of the session.

<figure class="shot"><img src="/media/connections-for-developers.webp" alt="For developers on the Connections page: the MCP and WebDriver addresses sit under the keys." width="1440" height="670" loading="lazy" /><figcaption>For developers on the Connections page: the MCP and WebDriver addresses sit under the keys.</figcaption></figure>

## Setup

1. **Check the endpoint.** `GET http://127.0.0.1:47800/wd/hub/status` needs no key and answers `"ready": true`. The real port is in `~/Library/Application Support/Silan/engine.json`.
2. **Make a key** on **Connections**, under **For developers**, with **New key**. Send it on every other call as `Authorization: Bearer silan_sk_...`. Without it a call answers `401`.
3. **Open a session.** `POST /wd/hub/session` with W3C capabilities:

   ```json
   {
     "capabilities": {
       "alwaysMatch": {
         "platformName": "iOS",
         "appium:udid": "00008120-001A2B3C4D5E6F70",
         "appium:automationName": "Silan",
         "appium:bundleId": "com.example.megamart"
       }
     }
   }
   ```

   The device comes from `silan:deviceId`, `appium:udid` or `udid`: Silan's full id, or the part after `ios:` or `android:`. With no device named, the session takes the first free device of `platformName`. The session claims the device with a lease of one hour, and `appium:bundleId` or `appium:appPackage` opens that app first. The answer carries `sessionId`, `silan:deviceId` and `silan:leaseId`.
4. **Find elements.** `POST /wd/hub/session/{id}/element` (or `/elements`) with `using` and `value`:

   | Strategy | What it matches |
   |---|---|
   | `accessibility id` | The element's identifier, then its text |
   | `id` | The identifier |
   | `name`, `link text` | The text, exactly |
   | `partial link text` | The text, containing the value |
   | `class name` | A role, from the class name |

   Any other strategy answers `invalid selector`. The find waits up to the session's implicit wait.
5. **Act.** On an element: click, send keys, clear, and read its text, attributes, rect, displayed, enabled, selected and tag name. On the session: screenshot, page source, window rect, W3C actions with touch pointers and keys, url, back, orientation, and `mobile:` scripts through execute: `pressButton`, `launchApp`, `terminateApp`, `swipe` or `scroll`, `deepLink`, `getClipboard` and `setClipboard`.
6. **End the session.** `DELETE /wd/hub/session/{id}` releases the lease.

## Try it

From a shell, open a session, take a screenshot and end it:

```sh
KEY=silan_sk_...
B=http://127.0.0.1:47800/wd/hub
curl -s "$B/status"
curl -s -H "Authorization: Bearer $KEY" -H 'Content-Type: application/json' \
  -d '{"capabilities":{"alwaysMatch":{"platformName":"iOS","appium:udid":"00008120-001A2B3C4D5E6F70","appium:automationName":"Silan"}}}' \
  "$B/session"
curl -s -H "Authorization: Bearer $KEY" "$B/session/$SESSION/screenshot"
curl -s -X DELETE -H "Authorization: Bearer $KEY" "$B/session/$SESSION"
```

The status answers `"ready": true`. The session answers a `sessionId` and `"silan:deviceId": "ios:..."`. The screenshot answers a base64 PNG under `value`. The delete answers `200`.

## Limits

- Not a full Appium server. Commands outside the list above answer `unknown command` or `unsupported operation`.
- A risky step answers `500` with the message `APPROVAL_REQUIRED: the person must approve this action in Silan`. Say yes in Silan, then send the same command again.
- Element commands need the element list: a phone on the Developer way, a simulator or an emulator. On a phone without Silan's helper, and on the Simple way, they answer `500 unsupported operation` (`CAPABILITY_UNSUPPORTED`). Launch, screenshots and the session itself still work.
- A device that another lease holds answers `session not created` with `DEVICE_LEASED`. A person's own step holds a device for 30 seconds after it.
- A session is tied to the key that made it. Revoking the key ends it.
- Error codes come back in WebDriver's words: `no such element` for `TARGET_NOT_FOUND`, `stale element reference` for `STALE_ELEMENT`, `timeout` for a wait that ran out.

:::note[Proof]
Proved on an iPhone 11 (iOS 27) on a cable, 14 Oct 2026, by a Silan worker: `/wd/hub/status` ready with no key, `401` for a session with no key, a session with `silan:deviceId`, its screenshot and its end with `200`. Run `20261014-154515-65263`. With no phone, a key's session answered `500 session not created` (`DEVICE_NOT_FOUND`) and `401` after Revoke, 14 and 24 Oct 2026, runs `20261014-190535-89037` and `20261024-042236-97156`. Source: verify-silan feature map, api and connections.
:::