> Part of the walkerOS documentation. Project overview and full index: <https://www.walkeros.io/llms.txt>

# Heap

[Web](#)[ Source code](https://github.com/elbwalker/walkerOS/tree/main/packages/web/destinations/heap)[ Package](https://www.npmjs.com/package/@walkeros/web-destination-heap) Beta

[Heap](https://www.heap.io/) (now part of Contentsquare) is a product analytics platform with auto-capture and retroactive event definitions. With `loadScript: true`, this destination loads the Heap script from Heap's CDN. It forwards walkerOS events through `heap.track(name, properties)`, plus identity, user properties, persistent event properties, and runtime consent.

Where this fits

Heap is a **web destination** in the walkerOS flow:

## Installation[​](#installation "Direct link to Installation")

```bash
npm install @walkeros/web-destination-heap
```

* Integrated
* Bundled

```typescript
import { startFlow } from '@walkeros/collector';
import { destinationHeap } from '@walkeros/web-destination-heap';

await startFlow({
  destinations: {
    heap: {
      code: destinationHeap,
      config: {
        loadScript: true,
        settings: {
          appId: 'YOUR_HEAP_APP_ID',
        },
      },
    },
  },
});
```

Add to your `flow.json` destinations:

```json
"destinations": {
  "heap": {
    "package": "@walkeros/web-destination-heap",
    "config": {
      "loadScript": true,
      "settings": {
        "appId": "YOUR_HEAP_APP_ID"
      }
    }
  }
}
```

[See bundled mode setup](/preview/pr-749/docs/getting-started/modes/bundled.md) | [CLI reference](/preview/pr-749/docs/apps/cli.md)

caution

`loadScript: true` loads the classic Heap SDK (`https://cdn.heapanalytics.com/js/heap-<appId>.js`). That SDK does not replay the calls the destination queues before it finishes loading, so events pushed earlier (such as the first page view) do not reach Heap. It also ignores `disablePageviewAutocapture`, `disableSessionReplay`, and `ingestServer`, and has no `startTracking()` or `stopTracking()`, so any consent command after it has loaded stops all further Heap events (see Consent).

## Configuration[​](#configuration "Direct link to Configuration")

This destination uses the standard destination config wrapper (consent, data, env, id, ...). For the shared fields see [destination configuration](/docs/destinations#configuration). Package-specific fields live under `config.settings` and are listed below.

## Settings[​](#settings "Direct link to Settings")

| Property                     | Type                  | Description                                                                                                                                                                | More |
| ---------------------------- | --------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---- |
| `appId*`                     | `string`              | Heap App ID. Find it in your Heap project under Settings > App ID.                                                                                                         |      |
| `disableTextCapture`         | `boolean`             | Disable Heap auto text capture. Default: true.                                                                                                                             |      |
| `disablePageviewAutocapture` | `boolean`             | Disable Heap automatic pageview tracking. Default: true (walkerOS sources handle pageviews). Passed to heap.load(); the classic Heap SDK that loadScript loads ignores it. |      |
| `disableSessionReplay`       | `boolean`             | Disable Heap session replay. Passed to heap.load(); the classic Heap SDK that loadScript loads ignores it.                                                                 |      |
| `secureCookie`               | `boolean`             | SSL-only cookies.                                                                                                                                                          |      |
| `ingestServer`               | `string`              | Custom server endpoint for proxying Heap data. Passed to heap.load(); the classic Heap SDK that loadScript loads ignores it.                                               |      |
| `heapConfig`                 | `Record<string, any>` | Additional heap.load() options, merged over the options above. Example: { "trackingServer": "https\://heap.example.com" }.                                                 |      |
| `identify`                   | `any`                 | Destination-level identity mapping. Resolves to a string for heap.identify(). Example: { "key": "user.id" }.                                                               |      |
| `userProperties`             | `any`                 | Destination-level user properties mapping. Resolves to object for heap.addUserProperties(). Example: { "map": { "plan": "data.plan" } }.                                   |      |

\* Required fields

## Mapping[​](#mapping "Direct link to Mapping")

Per-event rules under `config.mapping`. For the standard rule fields (consent, condition, data, batch, name, policy) see [mapping](/docs/mapping).

| Property               | Type  | Description                                                                                                            | More |
| ---------------------- | ----- | ---------------------------------------------------------------------------------------------------------------------- | ---- |
| `identify`             | `any` | Per-event identity. Resolves to a string for heap.identify(). Example: { "key": "data.email" }.                        |      |
| `reset`                | `any` | Reset Heap identity on this event. Set to true to call heap.resetIdentity().                                           |      |
| `userProperties`       | `any` | Per-event user properties. Resolves to object for heap.addUserProperties().                                            |      |
| `eventProperties`      | `any` | Per-event persistent event properties. Resolves to object for heap.addEventProperties() (persisted across page loads). |      |
| `clearEventProperties` | `any` | Clear all persistent event properties. Set to true to call heap.clearEventProperties().                                |      |

## Examples

### Consent granted

With consent: { analytics: true } in the destination config, a walker consent grant for analytics calls heap.startTracking.

Event

```json
{
  "analytics": true
}
```

Out

```javascript
heap.startTracking()
```

### Consent revoked

With consent: { analytics: true } in the destination config, the destination calls heap.startTracking once analytics is granted and heap.stopTracking when it is revoked. The classic Heap SDK that loadScript loads has neither method.

Event

```json
{
  "analytics": false
}
```

Out

```javascript
heap.startTracking();

heap.stopTracking()
```

### Default track

A walker event becomes a Heap track call with the event name and empty properties.

Event

```json
{
  "name": "product view",
  "data": {
    "id": "ers",
    "name": "Everyday Ruck Snack",
    "color": "black",
    "size": "l",
    "price": 420
  },
  "context": {
    "shopping": [
      "detail",
      0
    ]
  },
  "globals": {
    "pagegroup": "shop"
  },
  "custom": {
    "completely": "random"
  },
  "user": {
    "id": "us3r",
    "device": "c00k13",
    "session": "s3ss10n"
  },
  "nested": [],
  "consent": {
    "functional": true
  },
  "id": "e131862053adef0a",
  "trigger": "load",
  "entity": "product",
  "action": "view",
  "timestamp": 1700000100,
  "timing": 3.14,
  "source": {
    "count": 1,
    "trace": "0a1b2c3d4e5f60718293a4b5c6d7e8f9",
    "type": "collector",
    "schema": "4"
  }
}
```

Out

```javascript
heap.track("product view", {})
```

### Destination identify

With settings.identify set to 'user.id', the destination calls heap.identify before the default track.

Event

```json
{
  "name": "page view",
  "data": {
    "domain": "www.example.com",
    "title": "walkerOS documentation",
    "referrer": "https://www.walkeros.io/",
    "search": "?foo=bar",
    "hash": "#hash",
    "id": "/docs/"
  },
  "context": {
    "dev": [
      "test",
      1
    ]
  },
  "globals": {
    "pagegroup": "docs"
  },
  "custom": {
    "completely": "random"
  },
  "user": {
    "id": "us3r",
    "device": "c00k13",
    "session": "s3ss10n"
  },
  "nested": [
    {
      "entity": "child",
      "data": {
        "is": "subordinated"
      }
    }
  ],
  "consent": {
    "functional": true
  },
  "id": "0474dc6b65de51c0",
  "trigger": "load",
  "entity": "page",
  "action": "view",
  "timestamp": 1700000102,
  "timing": 3.14,
  "source": {
    "count": 1,
    "trace": "0a1b2c3d4e5f60718293a4b5c6d7e8f9",
    "type": "collector",
    "schema": "4"
  }
}
```

Out

```javascript
heap.identify("us3r");

heap.track("page view", {})
```

### Renamed purchase

An order complete is renamed to purchase and mapped to Heap track properties such as order\_id, total, and currency.

Event

```json
{
  "name": "order complete",
  "data": {
    "id": "0rd3r1d",
    "currency": "EUR",
    "shipping": 5.22,
    "taxes": 73.76,
    "total": 555
  },
  "context": {
    "shopping": [
      "complete",
      0
    ]
  },
  "globals": {
    "pagegroup": "shop"
  },
  "custom": {
    "completely": "random"
  },
  "user": {
    "id": "us3r",
    "device": "c00k13",
    "session": "s3ss10n"
  },
  "nested": [
    {
      "entity": "product",
      "data": {
        "id": "ers",
        "name": "Everyday Ruck Snack",
        "color": "black",
        "size": "l",
        "price": 420
      },
      "context": {
        "shopping": [
          "complete",
          0
        ]
      },
      "nested": []
    },
    {
      "entity": "product",
      "data": {
        "id": "cc",
        "name": "Cool Cap",
        "size": "one size",
        "price": 42
      },
      "context": {
        "shopping": [
          "complete",
          0
        ]
      },
      "nested": []
    },
    {
      "entity": "gift",
      "data": {
        "name": "Surprise"
      },
      "context": {
        "shopping": [
          "complete",
          0
        ]
      },
      "nested": []
    }
  ],
  "consent": {
    "functional": true
  },
  "id": "95b2d7e59e2ce094",
  "trigger": "load",
  "entity": "order",
  "action": "complete",
  "timestamp": 1700000101,
  "timing": 3.14,
  "source": {
    "count": 1,
    "trace": "0a1b2c3d4e5f60718293a4b5c6d7e8f9",
    "type": "collector",
    "schema": "4"
  }
}
```

Mapping

```json
{
  "name": "purchase",
  "data": {
    "map": {
      "order_id": "data.id",
      "total": "data.total",
      "currency": {
        "key": "data.currency",
        "value": "EUR"
      }
    }
  }
}
```

Out

```javascript
heap.track("purchase", {
  "order_id": "0rd3r1d",
  "total": 555,
  "currency": "EUR"
})
```

### User properties on event

An order fires Heap addUserProperties with last-order fields and then tracks the event.

Event

```json
{
  "name": "order complete",
  "data": {
    "id": "0rd3r1d",
    "total": 555,
    "currency": "EUR"
  },
  "context": {
    "shopping": [
      "complete",
      0
    ]
  },
  "globals": {
    "pagegroup": "shop"
  },
  "custom": {
    "completely": "random"
  },
  "user": {
    "id": "us3r",
    "device": "c00k13",
    "session": "s3ss10n"
  },
  "nested": [
    {
      "entity": "product",
      "data": {
        "id": "ers",
        "name": "Everyday Ruck Snack",
        "color": "black",
        "size": "l",
        "price": 420
      },
      "context": {
        "shopping": [
          "complete",
          0
        ]
      },
      "nested": []
    },
    {
      "entity": "product",
      "data": {
        "id": "cc",
        "name": "Cool Cap",
        "size": "one size",
        "price": 42
      },
      "context": {
        "shopping": [
          "complete",
          0
        ]
      },
      "nested": []
    },
    {
      "entity": "gift",
      "data": {
        "name": "Surprise"
      },
      "context": {
        "shopping": [
          "complete",
          0
        ]
      },
      "nested": []
    }
  ],
  "consent": {
    "functional": true
  },
  "id": "3f227884f15ee8f1",
  "trigger": "load",
  "entity": "order",
  "action": "complete",
  "timestamp": 1700000105,
  "timing": 3.14,
  "source": {
    "count": 1,
    "trace": "0a1b2c3d4e5f60718293a4b5c6d7e8f9",
    "type": "collector",
    "schema": "4"
  }
}
```

Mapping

```json
{
  "settings": {
    "userProperties": {
      "map": {
        "last_order_value": "data.total",
        "last_order_currency": "data.currency"
      }
    }
  }
}
```

Out

```javascript
heap.addUserProperties({
  "last_order_value": 555,
  "last_order_currency": "EUR"
});

heap.track("order complete", {})
```

### Global event properties

A page view sets persistent Heap event properties so all subsequent events include the page category.

Event

```json
{
  "name": "page view",
  "data": {
    "category": "docs"
  },
  "context": {
    "dev": [
      "test",
      1
    ]
  },
  "globals": {
    "pagegroup": "docs"
  },
  "custom": {
    "completely": "random"
  },
  "user": {
    "id": "us3r",
    "device": "c00k13",
    "session": "s3ss10n"
  },
  "nested": [
    {
      "entity": "child",
      "data": {
        "is": "subordinated"
      }
    }
  ],
  "consent": {
    "functional": true
  },
  "id": "85ff0c61027934a7",
  "trigger": "load",
  "entity": "page",
  "action": "view",
  "timestamp": 1700000106,
  "timing": 3.14,
  "source": {
    "count": 1,
    "trace": "0a1b2c3d4e5f60718293a4b5c6d7e8f9",
    "type": "collector",
    "schema": "4"
  }
}
```

Mapping

```json
{
  "silent": true,
  "settings": {
    "eventProperties": {
      "map": {
        "page_category": "data.category"
      }
    }
  }
}
```

Out

```javascript
heap.addEventProperties({
  "page_category": "docs"
})
```

### User login identify

A user login identifies the Heap user by email and adds user properties while skipping the track.

Event

```json
{
  "name": "user login",
  "data": {
    "email": "user@example.com",
    "plan": "premium",
    "company": "Acme"
  },
  "context": {
    "dev": [
      "test",
      1
    ]
  },
  "globals": {
    "lang": "elb"
  },
  "custom": {
    "completely": "random"
  },
  "user": {
    "id": "us3r",
    "device": "c00k13",
    "session": "s3ss10n"
  },
  "nested": [
    {
      "entity": "child",
      "data": {
        "is": "subordinated"
      }
    }
  ],
  "consent": {
    "functional": true
  },
  "id": "b8517a745dbbbd32",
  "trigger": "test",
  "entity": "user",
  "action": "login",
  "timestamp": 1700000103,
  "timing": 3.14,
  "source": {
    "count": 1,
    "trace": "0a1b2c3d4e5f60718293a4b5c6d7e8f9",
    "type": "collector",
    "schema": "4"
  }
}
```

Mapping

```json
{
  "silent": true,
  "settings": {
    "identify": "data.email",
    "userProperties": {
      "map": {
        "plan": "data.plan",
        "company": "data.company"
      }
    }
  }
}
```

Out

```javascript
heap.identify("user@example.com");

heap.addUserProperties({
  "plan": "premium",
  "company": "Acme"
})
```

### User logout reset

A user logout calls heap.resetIdentity to clear the identified user from the Heap client.

Event

```json
{
  "name": "user logout",
  "data": {
    "string": "foo",
    "number": 1,
    "boolean": true,
    "array": [
      0,
      "text",
      false
    ]
  },
  "context": {
    "dev": [
      "test",
      1
    ]
  },
  "globals": {
    "lang": "elb"
  },
  "custom": {
    "completely": "random"
  },
  "user": {
    "id": "us3r",
    "device": "c00k13",
    "session": "s3ss10n"
  },
  "nested": [
    {
      "entity": "child",
      "data": {
        "is": "subordinated"
      }
    }
  ],
  "consent": {
    "functional": true
  },
  "id": "d6224500fb023f74",
  "trigger": "test",
  "entity": "user",
  "action": "logout",
  "timestamp": 1700000104,
  "timing": 3.14,
  "source": {
    "count": 1,
    "trace": "0a1b2c3d4e5f60718293a4b5c6d7e8f9",
    "type": "collector",
    "schema": "4"
  }
}
```

Mapping

```json
{
  "silent": true,
  "settings": {
    "reset": true
  }
}
```

Out

```javascript
heap.resetIdentity()
```

## Consent[​](#consent "Direct link to Consent")

With `config.consent` declared, the destination is not loaded before the required consent keys are granted (see [consent management](/preview/pr-749/docs/guides/consent.md)). On each consent command it calls `heap.startTracking()` when all required keys are granted and `heap.stopTracking()` otherwise.

The classic SDK that `loadScript: true` loads has neither method. The grant that loads Heap works, but once the script has loaded, every later `walker consent` command, even one that repeats the current state, makes the destination's `on('consent')` handler fail. The collector logs `on callback failed` and holds the destination's events in its queue, so Heap receives nothing for the rest of the page view, while `elb` still returns `ok`. A revocation also does not stop Heap's own capture. This setup keeps sending events only when your consent source sends no further consent command after the grant that loads Heap.

```typescript
destinations: {
  heap: {
    code: destinationHeap,
    config: {
      loadScript: true,
      consent: { analytics: true },
      settings: { appId: 'YOUR_HEAP_APP_ID' },
    },
  },
}
```
