# Device Storage

Persist key–value data on this device. Survives Desktop Agent restarts, uninstalls, and reinstalls. Stays on this device; data is never sent to ZeroWork servers.

**API** `zw.deviceStorage.*`

```js
const credentials = {
  username: await zw.deviceStorage.get("username_salesforce"),
  password: await zw.deviceStorage.get("password_salesforce"),
};
await zw.deviceStorage.set("last_login_salesforce", String(Date.now()));
```

***

#### At a Glance

* <mark style="color:$info;">async</mark>\
  `await zw.deviceStorage.get(key: string)` \
  → returns the `string` value for the key, or `undefined` if not set.
* <mark style="color:$info;">async</mark>\
  `await zw.deviceStorage.set(key: string, value: string)` \
  → saves a `string` value under the key.
* <mark style="color:$info;">async</mark>\
  `await zw.deviceStorage.remove(key: string)` \
  → removes the key and its value from device storage.
* <mark style="color:$info;">async</mark>\
  `await zw.deviceStorage.has(key: string)` \
  → returns `true` if the key exists, otherwise `false`.
* <mark style="color:$info;">async</mark>\
  `await zw.deviceStorage.getAll()` \
  → returns an object of key–value pairs `{ [key: string]: string }`.

***

#### **Adding Secrets**

For passwords, API tokens, and other sensitive values, add them in the Desktop Agent UI instead of saving them as plain text in the **Write JS** building block using `zw.deviceStorage.set()`. (Reading a value with `zw.deviceStorage.get()` is safe.)

To add a secret:

1. Open the Desktop Agent.
2. Select **Device storage** from the tray menu.
3. Click **Add key**, then save your value.

<figure><img src="https://3220183989-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2Fg9LcR8XM7TGOzOCDGsCS%2Fuploads%2FmYCLdaNReWu0Vpw5U0oo%2FCleanShot%202026-01-04%20at%2014.19.19.png?alt=media&#x26;token=5c636ccb-0835-48ad-bd8e-3a86ff93b86a" alt=""><figcaption></figcaption></figure>

***

#### **Examples**

**Check if value is present and set if not**

```typescript
if (!(await zw.deviceStorage.has("machine_id"))) {
  await zw.deviceStorage.set("machine_id", "M1234");
}
```

**Securely log in with credentials stored only locally**

```js
// @zw-run-locally

const credentials = {
  username: await zw.deviceStorage.get("username_salesforce"),
  password: await zw.deviceStorage.get("password_salesforce"),
};

// Get the active page and enter credentials programmatically
// Alternatively, use zw.deviceStorage.* directly in no-code blocks
const page = await zw.browserContext.getActivePage();
await page.goto("https://login.salesforce.com/");
await page.type("#username", credentials.username);
await page.type("#password", credentials.password);
```

**Clear device storage**

```js
// Get an object mapping keys to string values
const all = await zw.deviceStorage.getAll();
await zw.log("deviceStorage keys", Object.keys(all));

const keys = Object.keys(all);
for (const key of keys) {
  await zw.deviceStorage.remove(key);
}
```

***

#### Saving Non-String Values

Device storage only accepts strings. You can **stringify on write** and **parse on read** if you want to work with other types.

```js
// Save an object (stringify on write)
const runMeta = {
  machineId: "M1234",
  lastRunAt: Date.now(),
  lastProcessedInvoiceId: "INV-2026-00127",
};
await zw.deviceStorage.set("billing_bot_state", JSON.stringify(runMeta));

// Read it back (parse on read)
const raw = await zw.deviceStorage.get("billing_bot_state");
const parsed = raw ? JSON.parse(raw) : null;
```

***

### **Logging Secrets**

If you need to log sensitive values (like passwords or API tokens), use `zw.logTemp()` instead of `zw.log()`. `zw.logTemp()` shows the value in the live run logs, but it is **not** saved to TaskBot run reports.

```javascript
const secret = await zw.deviceStorage.get("some_secret");

// ✅ Good: Visible in live logs only (not saved to run reports)
await zw.logTemp(secret);

// 🚫 Avoid: Saved to TaskBot run reports
await zw.log(secret);
```

⚠️ Avoid using `zw.deviceStorage.get()` to read secrets via [#code-in-inputs](https://docs.zerowork.io/using-zerowork/dynamic-inputs#code-in-inputs "mention") in no-code building blocks. Many building blocks auto-log values, and those logs are saved to TaskBot run reports.

***

#### Notes

* **Size limits.**\
  Total device storage is limited to **\~3,000,000 characters**. Writes that would exceed the total limit will throw an error.
* **Always use `await`.**\
  All methods in `zw.deviceStorage.*` are async, whether the code runs locally or in the browser.&#x20;

  ```js
  // 🚫 Won't work
  const someValue = zw.deviceStorage.get("someKey"); // missing await

  // ✅ Works
  const someValue = await zw.deviceStorage.get("someKey");
  ```
