# 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="/files/VU3dFuGO1X3QJC83zMuy" 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 [Dynamic Inputs](/using-zerowork/using-building-blocks/dynamic-inputs.md#code-in-inputs) 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");
  ```


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://docs.zerowork.io/using-zerowork/using-building-blocks/write-javascript/device-storage.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
