|
| 1 | +--- |
| 2 | +layout: userid |
| 3 | +title: LocID |
| 4 | +description: LocID User ID sub-module |
| 5 | +useridmodule: locIdSystem |
| 6 | +bidRequestUserId: locId |
| 7 | +eidsource: locid.com |
| 8 | +example: '"SYybozbTuRaZkgGqCD7L7EE0FncoNUcx-om4xTfhJt36TFIAES2tF1qPH"' |
| 9 | +--- |
| 10 | + |
| 11 | +## Overview |
| 12 | + |
| 13 | +LocID is a geospatial identifier provided by Digital Envoy. The LocID User ID submodule retrieves a LocID from a publisher-controlled first-party endpoint, respects applicable privacy framework restrictions, and exposes the identifier to bidders via the standard EIDs interface. |
| 14 | + |
| 15 | +The endpoint is a first-party or on-premises service operated by the publisher, GrowthCode, or Digital Envoy. The module does not transmit IP addresses from the browser; instead, the server-side endpoint derives location information. |
| 16 | + |
| 17 | +## Registration |
| 18 | + |
| 19 | +No registration is required to use this module. Publishers must configure a first-party endpoint that proxies requests to the LocID encryption service. |
| 20 | + |
| 21 | +## Installation |
| 22 | + |
| 23 | +Build Prebid.js with the LocID module: |
| 24 | + |
| 25 | +```bash |
| 26 | +gulp build --modules=locIdSystem,userId |
| 27 | +``` |
| 28 | + |
| 29 | +## Configuration |
| 30 | + |
| 31 | +### Default Mode |
| 32 | + |
| 33 | +By default, the module proceeds when no privacy framework signals are present (LI-based operation): |
| 34 | + |
| 35 | +```javascript |
| 36 | +pbjs.setConfig({ |
| 37 | + userSync: { |
| 38 | + userIds: [{ |
| 39 | + name: 'locId', |
| 40 | + params: { |
| 41 | + endpoint: 'https://id.example.com/locid', |
| 42 | + ipEndpoint: 'https://id.example.com/ip' // optional: lightweight IP-only check |
| 43 | + }, |
| 44 | + storage: { |
| 45 | + type: 'html5', |
| 46 | + name: '_locid', |
| 47 | + expires: 7 |
| 48 | + } |
| 49 | + }] |
| 50 | + } |
| 51 | +}); |
| 52 | +``` |
| 53 | + |
| 54 | +### Strict Mode |
| 55 | + |
| 56 | +To require privacy framework signals before proceeding, set `privacyMode: 'requireSignals'`: |
| 57 | + |
| 58 | +```javascript |
| 59 | +pbjs.setConfig({ |
| 60 | + userSync: { |
| 61 | + userIds: [{ |
| 62 | + name: 'locId', |
| 63 | + params: { |
| 64 | + endpoint: 'https://id.example.com/locid', |
| 65 | + privacyMode: 'requireSignals' |
| 66 | + }, |
| 67 | + storage: { |
| 68 | + type: 'html5', |
| 69 | + name: '_locid', |
| 70 | + expires: 7 |
| 71 | + } |
| 72 | + }] |
| 73 | + } |
| 74 | +}); |
| 75 | +``` |
| 76 | + |
| 77 | +### Configuration with API Key and Alternative ID |
| 78 | + |
| 79 | +```javascript |
| 80 | +pbjs.setConfig({ |
| 81 | + userSync: { |
| 82 | + userIds: [{ |
| 83 | + name: 'locId', |
| 84 | + params: { |
| 85 | + endpoint: 'https://id.example.com/locid', |
| 86 | + apiKey: 'your-api-key', |
| 87 | + altId: 'publisher-user-id', |
| 88 | + timeoutMs: 1000, |
| 89 | + withCredentials: true |
| 90 | + }, |
| 91 | + storage: { |
| 92 | + type: 'html5', |
| 93 | + name: '_locid', |
| 94 | + expires: 7 |
| 95 | + } |
| 96 | + }] |
| 97 | + } |
| 98 | +}); |
| 99 | +``` |
| 100 | + |
| 101 | +## Parameters |
| 102 | + |
| 103 | +{: .table .table-bordered .table-striped } |
| 104 | + |
| 105 | +| Param | Scope | Type | Description | Example | |
| 106 | +| --- | --- | --- | --- | --- | |
| 107 | +| name | Required | String | Module identifier. Must be `"locId"`. | `"locId"` | |
| 108 | +| params | Required | Object | Configuration parameters. | | |
| 109 | +| params.endpoint | Required | String | First-party LocID endpoint URL. See Endpoint Requirements below. | `"https://id.example.com/locid"` | |
| 110 | +| params.ipEndpoint | Optional | String | Separate lightweight endpoint returning only the connection IP. Used for IP change detection without a full tx_cloc fetch. | `"https://id.example.com/ip"` | |
| 111 | +| params.ipCacheTtlMs | Optional | Number | TTL for the IP cache entry in milliseconds. | `14400000` (4 hours, default) | |
| 112 | +| params.altId | Optional | String | Alternative identifier appended as `?alt_id=` query parameter. | `"user123"` | |
| 113 | +| params.timeoutMs | Optional | Number | Request timeout in milliseconds. | `800` (default) | |
| 114 | +| params.withCredentials | Optional | Boolean | Include credentials (cookies) on the request. | `false` (default) | |
| 115 | +| params.apiKey | Optional | String | API key passed via the `x-api-key` request header. | `"your-api-key"` | |
| 116 | +| params.privacyMode | Optional | String | Privacy mode: `"allowWithoutSignals"` (default) or `"requireSignals"`. | `"allowWithoutSignals"` | |
| 117 | +| params.requirePrivacySignals | Optional | Boolean | If `true`, requires privacy signals to be present. Equivalent to `privacyMode: 'requireSignals'`. | `false` (default) | |
| 118 | +| storage | Required | Object | Storage configuration for caching the ID. | | |
| 119 | +| storage.type | Required | String | Storage type. Use `"html5"` for localStorage. | `"html5"` | |
| 120 | +| storage.name | Required | String | Storage key name. | `"_locid"` | |
| 121 | +| storage.expires | Optional | Number | TTL in days. | `7` | |
| 122 | + |
| 123 | +## Endpoint Requirements |
| 124 | + |
| 125 | +The `endpoint` parameter must point to a first-party proxy or on-premises service, not the LocID Encrypt API directly. |
| 126 | + |
| 127 | +The LocID Encrypt API requires the client IP address as a parameter. Since browsers cannot determine their own public IP, a server-side proxy is required to: |
| 128 | + |
| 129 | +1. Receive the request from the browser |
| 130 | +2. Extract the client IP from the incoming connection |
| 131 | +3. Forward the request to the LocID Encrypt API with the IP injected |
| 132 | +4. Return the response (`tx_cloc`, `connection_ip`) to the browser |
| 133 | + |
| 134 | +If `altId` is configured, the module appends it as `?alt_id=<value>` to the endpoint URL. |
| 135 | + |
| 136 | +## Privacy Handling |
| 137 | + |
| 138 | +LocID operates under Legitimate Interest (LI). The module's privacy behavior depends on the configured privacy mode. |
| 139 | + |
| 140 | +### Default Behavior (allowWithoutSignals) |
| 141 | + |
| 142 | +- **No privacy signals present**: Module proceeds and fetches the ID |
| 143 | +- **Privacy signals present**: Enforcement rules apply |
| 144 | + |
| 145 | +### Strict Mode (requireSignals) |
| 146 | + |
| 147 | +- **No privacy signals present**: Module returns `undefined` |
| 148 | +- **Privacy signals present**: Enforcement rules apply |
| 149 | + |
| 150 | +### Privacy Signal Enforcement |
| 151 | + |
| 152 | +When privacy signals are present, the module does not fetch or return an ID if any of the following apply: |
| 153 | + |
| 154 | +- GDPR applies and vendorData is present, but consentString is missing or empty |
| 155 | +- US Privacy (CCPA) string indicates a processing restriction (third character is `Y`) |
| 156 | +- GPP signals indicate an applicable processing restriction |
| 157 | + |
| 158 | +When GDPR applies and consentString is present, the module proceeds unless a framework processing restriction is signaled. |
| 159 | + |
| 160 | +## Storage |
| 161 | + |
| 162 | +The module caches the LocID using Prebid's standard storage framework. Configure storage settings via the `storage` object. |
| 163 | + |
| 164 | +The endpoint response contains: |
| 165 | + |
| 166 | +- `tx_cloc`: Transactional LocID (used as the EID value) |
| 167 | +- `connection_ip`: The resolved client IP address (used for IP-aware cache invalidation) |
| 168 | + |
| 169 | +The module only uses `tx_cloc` for the EID. Any `stable_cloc` in the response is ignored client-side; it is available for proxy/endpoint operators to use in their own caching strategies. |
| 170 | + |
| 171 | +### IP Change Detection |
| 172 | + |
| 173 | +The module uses a two-tier cache to detect IP changes without churning the tx_cloc identifier: |
| 174 | + |
| 175 | +- **IP cache** (default 4-hour TTL): Tracks the current connection IP in a separate localStorage key. |
| 176 | +- **tx_cloc cache** (configured via `storage.expires`): Stores the LocID via Prebid's userId framework. |
| 177 | + |
| 178 | +When the IP cache expires, the module refreshes the IP. If `ipEndpoint` is configured, it makes a lightweight IP-only check first and only calls the main endpoint when the IP has changed. If the IP is unchanged and the tx_cloc cache is still valid, the existing tx_cloc is reused without calling the main endpoint. |
| 179 | + |
| 180 | +## EID Output |
| 181 | + |
| 182 | +When available, the LocID is included in the bid request as: |
| 183 | + |
| 184 | +```json |
| 185 | +{ |
| 186 | + "source": "locid.com", |
| 187 | + "uids": [{ |
| 188 | + "id": "SYybozbTuRaZkgGqCD7L7EE0FncoNUcx-om4xTfhJt36TFIAES2tF1qPH", |
| 189 | + "atype": 1 |
| 190 | + }] |
| 191 | +} |
| 192 | +``` |
| 193 | + |
| 194 | +The `atype` value of `1` is the OpenRTB agent type for web environment; it is not an IAB GVL vendor ID. |
| 195 | + |
| 196 | +## Debugging |
| 197 | + |
| 198 | +```javascript |
| 199 | +// Check if LocID is available |
| 200 | +pbjs.getUserIds().locId |
| 201 | + |
| 202 | +// Force refresh |
| 203 | +pbjs.refreshUserIds() |
| 204 | + |
| 205 | +// Check stored value |
| 206 | +localStorage.getItem('_locid') |
| 207 | +``` |
0 commit comments