demand

Demand editing: demand points and pops (commuter groups) in the loaded city.

api.demand. 8 members. Pop mutations add and remove pop ids in the endpoint points' popIds and add or subtract the pop's size from their residents and jobs (never below 0). Every mutation commits through the store's demand setter with fresh Map instances, and onDemandChange fires once per committed call. Every method except setPersistence returns a result object and never throws. Every method except setPersistence is blocked while challenge data is present and neither completed nor submitted to the leaderboard. Edits are not saved by default. For a mod that calls setPersistence(true), its calls are recorded, field by field, into the save's mod demand overlay under the rules below and restored on load, whether or not the mod is installed then. A saved pop whose home or work point is missing on load is not applied; the next save keeps it. The save is built from those recorded values only, never from live demand, so an edit by a mod that did not opt in never reaches the save, even on a pop an opted-in mod also changed. An opted-in mod's change to a pop or point an opted-in mod added (this session or restored from the save) updates its saved entry. Other changes and removals are recorded only for the city's own pops and points. An opted-in mod removing a pop or point added this session or restored from the save drops it from the save. An edit is attributed to the mod that owns the demand object it is made through: the global object to the mod whose code is running synchronously, the object setPersistence returns to the mod that created it, whichever code calls it. Restored edits are already in demand when onGameLoaded fires; an opted-in mod that re-adds its pops on load adds them a second time. Never recorded: edits made synchronously inside onCityLoad or onGameLoaded callbacks, inside a mod script reloadMods re-runs, or inside a hook reloadMods replays. Values above these limits are rejected with success: false, never clamped: pop size at most 1000000, drivingSeconds at most 10000000, drivingDistance at most 50000000 (m), point residents and jobs at most 1000000000.

Method Reference#

MemberReturnsSummary
addDemandPoint(input)AddDemandPointResultCreate a demand point at [lng, lat] with no residents, no jobs and no pops.
updateDemandPoint(id, updates)UpdateDemandPointResultMove a demand point or override its displayed residents and jobs counts.
removeDemandPoint(id)RemoveDemandPointResultRemove a demand point and every pop that lives or works at it.
addPops(inputs)AddPopsResultAdd pops between existing demand points. All entries are validated before any is applied.
updatePop(id, updates)UpdatePopResultChange a pop's home point, work point and/or size, adjusting endpoint links and counts.
updatePops(entries)UpdatePopsResultApply many pop updates with one clone and one commit of the demand maps.
removePops(popIds)RemovePopsResultRemove pops by id. Unknown ids are skipped.
setPersistence(enabled)DemandAPIOpt the calling mod in to, or out of, saving its demand edits with the game. Off by default.

Data Model#

Demand is two linked structures, both returned by gameState.getDemandData():

StructureWhat it isKey fields
Demand point (ExtendedDemandPoint)A place on the map; draws as a demand bubble.id, location, residents, jobs, popIds
Pop (ExtendedPop)A group of commuters with one home and one workplace; pops are what ride the network.id, size, residenceId, jobId, drivingSeconds, drivingDistance

A point's residents and jobs size its bubble and feed the stats panels; they do not create commuters. A point with 1,000 jobs and no pop whose jobId is that point draws no riders. To fill a workplace, add pops whose jobId is the point and whose residenceId is an existing or new residential point; the pop methods add the pop's size to both points' counts and its id to their popIds.

Demand Points#

addDemandPoint#

api.demand.addDemandPoint(input: AddDemandPointInput): AddDemandPointResult

Create a demand point at [lng, lat] with no residents, no jobs and no pops.

An omitted id is generated as mod-point-<up to 8 random base-36 chars>. For a mod that opted in with setPersistence(true), the call is recorded for the save file at call time under the rules on setPersistence; edits by mods that did not opt in are never recorded. Nothing is recorded for a call made synchronously inside an onCityLoad or onGameLoaded callback, a mod script reloadMods re-runs, or a hook reloadMods replays.

Parameter input (AddDemandPointInput): location and optional id of the new point.

Returns: AddDemandPointResult. { success: true, pointId }; { success: false, error } on failure.

Errors:

  • active daily challenge (challenge data present and neither completed nor submitted to the leaderboard): logs [Modding API] demand.<method> is blocked during active challenges, returns { success: false, error: 'Demand edits are blocked during active challenges' }.
  • location missing, non-numeric, non-finite or outside [-180, 180] x [-90, 90]: returns error: 'location must be a [lng, lat] pair'.
  • id present but not a string: returns error: 'id must be a string'.
  • id already in use: returns error: 'Demand point "<id>" already exists'.
  • exception: logs [Modding API] Error adding demand point:, returns success: false with the exception message or Unknown error.

Hooks fired: onDemandChange (with the pop count, once per successful call).

Side effects: store demand data replaced; the point id recorded as added for the save overlay when the mod opted in.

Changed: For a mod that opted in with setPersistence(true), the edit is written to the save under modDemandOverlay and restored on load, whether or not the mod is installed then (PR #1090).

js
const r = api.demand.addDemandPoint({ id: 'stadium', location: [-73.926, 40.829] });
if (!r.success) console.log(r.error);
// a new point has 0 residents and 0 jobs until pops link to it

updateDemandPoint#

api.demand.updateDemandPoint(id: string, updates: UpdateDemandPointInput): UpdateDemandPointResult

Move a demand point or override its displayed residents and jobs counts.

An overridden count stays in place. Later pop mutations add or subtract pop sizes on top of it and never recompute it from the attached pops. Only the fields set in updates are saved for the point. On load a count that was not set keeps the value the city data and the restored pop edits give it. For a mod that opted in with setPersistence(true), the call is recorded for the save file at call time under the rules on setPersistence; edits by mods that did not opt in are never recorded. Nothing is recorded for a call made synchronously inside an onCityLoad or onGameLoaded callback, a mod script reloadMods re-runs, or a hook reloadMods replays.

ParamTypeDefaultDescription
idstringrequiredid of the point to update.
updatesUpdateDemandPointInputrequirednew location and/or count overrides.

Returns: UpdateDemandPointResult. { success: true }; { success: false, error } on failure.

Errors:

  • active daily challenge (challenge data present and neither completed nor submitted to the leaderboard): logs [Modding API] demand.<method> is blocked during active challenges, returns { success: false, error: 'Demand edits are blocked during active challenges' }.
  • updates missing or not an object: returns error: 'updates object is required'.
  • invalid location: returns error: 'location must be a [lng, lat] pair'.
  • residents not a finite number in [0, 1000000000]: returns error: 'residents must be a non-negative number up to 1000000000'.
  • jobs not a finite number in [0, 1000000000]: returns error: 'jobs must be a non-negative number up to 1000000000'.
  • unknown id: returns error: 'Demand point "<id>" not found'.
  • exception: logs [Modding API] Error updating demand point:, returns success: false with the exception message or Unknown error.

Hooks fired: onDemandChange (with the pop count, once per successful call).

Side effects: store demand data replaced; the point id recorded as modified for the save overlay when the mod opted in.

Changed: For a mod that opted in with setPersistence(true), the edit is written to the save under modDemandOverlay and restored on load, whether or not the mod is installed then (PR #1090).

js
const { pointId } = api.demand.addDemandPoint({ location: [-73.99, 40.75] });
api.demand.updateDemandPoint(pointId, { jobs: 2500, residents: 0 });

removeDemandPoint#

api.demand.removeDemandPoint(id: string): RemoveDemandPointResult

Remove a demand point and every pop that lives or works at it.

Affected pops are found from the point's popIds and from a scan of every pop's residenceId and jobId. Each removed pop is unlinked from its other endpoint, whose counts drop by the pop's size (never below 0). For a mod that opted in with setPersistence(true), the call is recorded for the save file at call time under the rules on setPersistence; edits by mods that did not opt in are never recorded. Nothing is recorded for a call made synchronously inside an onCityLoad or onGameLoaded callback, a mod script reloadMods re-runs, or a hook reloadMods replays. An opted-in mod's removal is recorded only for a pop or point of the city's own data; its removal of one added this session or restored from the save drops that entry from the save.

Parameter id (string): id of the point to remove.

Returns: RemoveDemandPointResult. { success: true, removedPopCount }; { success: false, error } on failure.

Errors:

  • active daily challenge (challenge data present and neither completed nor submitted to the leaderboard): logs [Modding API] demand.<method> is blocked during active challenges, returns { success: false, error: 'Demand edits are blocked during active challenges' }.
  • unknown id: returns error: 'Demand point "<id>" not found'.
  • exception: logs [Modding API] Error removing demand point:, returns success: false with the exception message or Unknown error.

Hooks fired: onDemandChange (with the pop count, once per successful call).

Side effects: store demand data replaced; the point and each removed pop recorded as removed for the save overlay when the mod opted in.

Changed: For a mod that opted in with setPersistence(true), the edit is written to the save under modDemandOverlay and restored on load, whether or not the mod is installed then (PR #1090).

js
const { pointId } = api.demand.addDemandPoint({ location: [-73.99, 40.75] });
const r = api.demand.removeDemandPoint(pointId);
// removes every pop living or working there too
console.log(`removed ${r.removedPopCount} pops`);

Pops#

addPops#

api.demand.addPops(inputs: AddPopInput[]): AddPopsResult

Add pops between existing demand points. All entries are validated before any is applied.

Each pop id is generated as mod-pop-<up to 8 random base-36 chars>. Omitted size is 200 people. When drivingSeconds or drivingDistance is omitted, the missing value is estimated: drivingDistance as the straight-line distance between the points times 1.3 (m); drivingSeconds as drivingDistance (supplied or estimated) divided by 11.1 m/s with a floor of 60 s. Commute departure times are assigned by the same routine city data goes through on load. Each endpoint point gains the pop id in popIds and its residents or jobs count grows by the pop size. For a mod that opted in with setPersistence(true), the call is recorded for the save file at call time under the rules on setPersistence; edits by mods that did not opt in are never recorded. Nothing is recorded for a call made synchronously inside an onCityLoad or onGameLoaded callback, a mod script reloadMods re-runs, or a hook reloadMods replays.

Parameter inputs (AddPopInput[]): one entry per pop to add; must be a non-empty array.

Returns: AddPopsResult. { success: true, popIds } in input order; { success: false, error } with nothing applied on failure.

Errors:

  • active daily challenge (challenge data present and neither completed nor submitted to the leaderboard): logs [Modding API] demand.<method> is blocked during active challenges, returns { success: false, error: 'Demand edits are blocked during active challenges' }.
  • inputs not a non-empty array: returns error: 'addPops requires a non-empty array'.
  • entry not an object: returns error: 'Entry <i>: must be an object'.
  • unknown residenceId: returns error: 'Entry <i>: residence point "<id>" not found'.
  • unknown jobId: returns error: 'Entry <i>: job point "<id>" not found'.
  • size not a finite number in (0, 1000000]: returns error: 'Entry <i>: size must be a positive number up to 1000000'.
  • drivingSeconds not a finite number in [0, 10000000], or drivingDistance not a finite number in [0, 50000000]: returns error: 'Entry <i>: drivingSeconds must be a non-negative number up to 10000000' or 'Entry <i>: drivingDistance must be a non-negative number up to 50000000'.
  • exception: logs [Modding API] Error adding pops:, returns success: false with the exception message or Unknown error.

Hooks fired: onDemandChange (with the pop count, once per successful call).

Side effects: store demand data replaced; each pop id recorded as added for the save overlay when the mod opted in.

Changed: For a mod that opted in with setPersistence(true), the edit is written to the save under modDemandOverlay and restored on load, whether or not the mod is installed then (PR #1090).

js
const home = api.demand.addDemandPoint({ location: [-73.95, 40.78] });
const work = api.demand.addDemandPoint({ location: [-74.01, 40.71] });
const r = api.demand.addPops([{ residenceId: home.pointId, jobId: work.pointId, size: 400 }]);
// r.popIds[0] starts with 'mod-pop-'; the home point now has residents 400, the work point jobs 400

updatePop#

api.demand.updatePop(id: string, updates: UpdatePopInput): UpdatePopResult

Change a pop's home point, work point and/or size, adjusting endpoint links and counts.

The pop is unlinked from its current endpoints and relinked with the new values, so a size-only change moves both endpoints' counts by the size delta unless the unlink hit the 0 floor. When the home or work point changes, drivingDistance and drivingSeconds are re-estimated from the straight-line distance (times 1.3; divided by 11.1 m/s; floor 60 s) and drivingPath, lastCommute and commutes are deleted. Each call clones both demand maps, so a loop over many pops repeats that cost per call. updatePops clones once. For a mod that opted in with setPersistence(true), the call is recorded for the save file at call time under the rules on setPersistence; edits by mods that did not opt in are never recorded. Nothing is recorded for a call made synchronously inside an onCityLoad or onGameLoaded callback, a mod script reloadMods re-runs, or a hook reloadMods replays. Only the fields set are saved (the re-estimated drive values too when the home or work point changed); the city's own values of the other fields are left as they are on load.

ParamTypeDefaultDescription
idstringrequiredid of the pop to update.
updatesUpdatePopInputrequirednew residenceId, jobId and/or size.

Returns: UpdatePopResult. { success: true }; { success: false, error } on failure.

Errors:

  • active daily challenge (challenge data present and neither completed nor submitted to the leaderboard): logs [Modding API] demand.<method> is blocked during active challenges, returns { success: false, error: 'Demand edits are blocked during active challenges' }.
  • updates missing or not an object: returns error: 'updates object is required'.
  • unknown pop: returns error: 'Pop "<id>" not found'.
  • unknown residenceId: returns error: 'Residence point "<id>" not found'.
  • unknown jobId: returns error: 'Job point "<id>" not found'.
  • size not a finite number in (0, 1000000]: returns error: 'size must be a positive number up to 1000000'.
  • exception: logs [Modding API] Error updating pop:, returns success: false with the exception message or Unknown error.

Hooks fired: onDemandChange (with the pop count, once per successful call).

Side effects: store demand data replaced; the pop id recorded as modified for the save overlay when the mod opted in.

Changed: For a mod that opted in with setPersistence(true), the edit is written to the save under modDemandOverlay and restored on load, whether or not the mod is installed then (PR #1090).

js
const home = api.demand.addDemandPoint({ location: [-73.95, 40.78] });
const work = api.demand.addDemandPoint({ location: [-74.01, 40.71] });
const other = api.demand.addDemandPoint({ location: [-73.98, 40.76] });
const { popIds } = api.demand.addPops([{ residenceId: home.pointId, jobId: work.pointId, size: 200 }]);
// move the job: driving time is re-estimated from straight-line distance
api.demand.updatePop(popIds[0], { jobId: other.pointId, size: 250 });

updatePops#

api.demand.updatePops(entries: UpdatePopsEntry[]): UpdatePopsResult

Apply many pop updates with one clone and one commit of the demand maps.

Each entry has the updatePop semantics plus the pop id. Entries apply in order on the same clone: a later entry sees earlier changes, and a later entry for the same id applies on top of the earlier one. An invalid entry discards the whole batch. For a mod that opted in with setPersistence(true), the call is recorded for the save file at call time under the rules on setPersistence; edits by mods that did not opt in are never recorded. Nothing is recorded for a call made synchronously inside an onCityLoad or onGameLoaded callback, a mod script reloadMods re-runs, or a hook reloadMods replays.

Parameter entries (UpdatePopsEntry[]): updates to apply in order; must be a non-empty array.

Returns: UpdatePopsResult. { success: true, updatedCount } where updatedCount is the number of entries; { success: false, error } with nothing applied on failure.

Errors:

  • active daily challenge (challenge data present and neither completed nor submitted to the leaderboard): logs [Modding API] demand.<method> is blocked during active challenges, returns { success: false, error: 'Demand edits are blocked during active challenges' }.
  • entries not a non-empty array: returns error: 'updatePops requires a non-empty array'.
  • entry not an object with a string id: returns error: 'Entry <i>: must be an object with a string id'.
  • any per-entry updatePop error: returns error: 'Entry <i>: <that message>'.
  • exception: logs [Modding API] Error updating pops:, returns success: false with the exception message or Unknown error.

Hooks fired: onDemandChange (with the pop count, once per successful call).

Side effects: store demand data replaced once; each entry recorded for the save under the setPersistence rules when the mod opted in.

Changed: For a mod that opted in with setPersistence(true), the edit is written to the save under modDemandOverlay and restored on load, whether or not the mod is installed then (PR #1090).

js
const home = api.demand.addDemandPoint({ location: [-73.95, 40.78] });
const work = api.demand.addDemandPoint({ location: [-74.01, 40.71] });
const { popIds } = api.demand.addPops([{ residenceId: home.pointId, jobId: work.pointId, size: 400 }]);
const r = api.demand.updatePops(popIds.map((id) => ({ id, size: 100 })));
// r.updatedCount === popIds.length; onDemandChange fired once

removePops#

api.demand.removePops(popIds: string[]): RemovePopsResult

Remove pops by id. Unknown ids are skipped.

Each removed pop is unlinked from both endpoints, whose counts drop by the pop's size (never below 0). The store is committed only when at least one pop was removed. For a mod that opted in with setPersistence(true), the call is recorded for the save file at call time under the rules on setPersistence; edits by mods that did not opt in are never recorded. Nothing is recorded for a call made synchronously inside an onCityLoad or onGameLoaded callback, a mod script reloadMods re-runs, or a hook reloadMods replays. An opted-in mod's removal is recorded only for a pop or point of the city's own data; its removal of one added this session or restored from the save drops that entry from the save.

Parameter popIds (string[]): ids of the pops to remove.

Returns: RemovePopsResult. { success: true, removedCount } where skipped ids count toward nothing; { success: false, error } on failure.

Errors:

  • active daily challenge (challenge data present and neither completed nor submitted to the leaderboard): logs [Modding API] demand.<method> is blocked during active challenges, returns { success: false, error: 'Demand edits are blocked during active challenges' }.
  • popIds not an array: returns error: 'removePops requires an array of pop ids'.
  • exception: logs [Modding API] Error removing pops:, returns success: false with the exception message or Unknown error.

Hooks fired: onDemandChange (with the pop count, only when removedCount is greater than 0).

Side effects: store demand data replaced when something was removed; each removed pop id recorded as removed for the save overlay when the mod opted in.

Changed: For a mod that opted in with setPersistence(true), the edit is written to the save under modDemandOverlay and restored on load, whether or not the mod is installed then (PR #1090).

js
const data = api.gameState.getDemandData();
if (data) {
    // drop every pop smaller than 5 people
    const tiny = [...data.popsMap].filter(([, pop]) => pop.size < 5).map(([id]) => id);
    const r = api.demand.removePops(tiny);
    console.log(`removed ${r.removedCount} pops`);
}

Saving Changes#

setPersistence#

api.demand.setPersistence(enabled: boolean): DemandAPI

Opt the calling mod in to, or out of, saving its demand edits with the game. Off by default.

The mod is the one whose code is running when this is called (for the object this returns, the mod it is bound to). With persistence on, the mod's calls are recorded, field by field, into the save's mod demand overlay under the rules below and restored on load, whether or not the mod is installed then. The save never reads live demand, so later edits by mods that did not opt in are not saved. Restored edits are already in demand when onGameLoaded fires. Check for the saved pop and point ids before re-adding on load; a re-added pop is added a second time. The global api.demand attributes an edit to the mod whose code is running synchronously, so its edits after an await or in a timer are not attributed to any mod and are not recorded. The returned object has the same methods as api.demand and attributes every edit to the bound mod from any async context, whichever code calls it. Never recorded: edits made synchronously inside onCityLoad or onGameLoaded callbacks, inside a mod script reloadMods re-runs, or inside a hook reloadMods replays. false stops recording. Entries already recorded or loaded from the save stay in later saves with their recorded values; the mod's later edits or removals of them are not saved. An opted-in mod's change to a pop or point an opted-in mod added (this session or restored from the save) updates its saved entry; other changes and removals are recorded only for the city's own pops and points. An opted-in mod removing a pop or point added this session or restored from the save drops it from the save. reloadMods clears every mod's opt-in. A mod that calls setPersistence(true) in its script opts in again when the script re-runs. A save whose overlay version is an integer above 1 (a newer build) freezes recording until another game or city loads, and that overlay is written back unchanged. Any other unexpected version is ignored and recording goes on. For a point changed with updateDemandPoint, only the fields set are saved.

Parameter enabled (boolean): true to record this mod's demand edits; anything else stops recording.

Returns: DemandAPI. a demand object bound to the calling mod; the object it was called on when called outside mod context.

Errors: called outside mod context: logs [Modding API] demand.setPersistence() called outside of mod context; demand edits will not be saved, changes nothing, returns the object it was called on.

Side effects: adds or removes the mod id in the set of mods whose demand edits are saved.

Changed: new in PR #1090.

js
const demand = api.demand.setPersistence(true); // at the top of the script
api.hooks.onGameInit(async () => {
    const home = demand.addDemandPoint({ location: [-73.95, 40.78] });
    const work = demand.addDemandPoint({ location: [-74.01, 40.71] });
    const route = await api.map.queryRoute(api.utils.getCityCode(), [-73.95, 40.78], [-74.01, 40.71]);
    // after the await only the returned object still records edits for this mod
    demand.addPops([{ residenceId: home.pointId, jobId: work.pointId, size: 50, ...(route && { drivingSeconds: route.drivingSeconds, drivingDistance: route.drivingDistance }) }]);
});

AddDemandPointInput#

Input for demand.addDemandPoint.

FieldTypeDefaultDescription
idstringnonePoint id; must be a string not already in use; generated as mod-point-<up to 8 random base-36 chars> when omitted.
location[number, number]required[lng, lat]; degrees; lng within [-180, 180], lat within [-90, 90], both finite.

AddDemandPointResult#

Result of demand.addDemandPoint.

FieldTypeDefaultDescription
successbooleanrequiredtrue when the point was created.
pointIdstringnoneId of the created point; absent on failure.
errorstringnoneFailure reason; absent on success.

UpdateDemandPointInput#

Input for demand.updateDemandPoint. Every field is optional.

FieldTypeDefaultDescription
location[number, number]noneNew [lng, lat]; degrees; same bounds as AddDemandPointInput.location.
residentsnumbernoneOverride for the point's resident count; people; finite, >= 0 and at most 1000000000; otherwise maintained from the attached pops.
jobsnumbernoneOverride for the job count; people; finite, >= 0 and at most 1000000000; otherwise maintained from the attached pops.

UpdateDemandPointResult#

Result of demand.updateDemandPoint.

FieldTypeDefaultDescription
successbooleanrequiredtrue when the point was updated.
errorstringnoneFailure reason; absent on success.

RemoveDemandPointResult#

Result of demand.removeDemandPoint.

FieldTypeDefaultDescription
successbooleanrequiredtrue when the point was removed.
removedPopCountnumbernonePops that lived or worked at the point and were removed with it; absent on failure.
errorstringnoneFailure reason; absent on success.

AddPopInput#

One pop for demand.addPops.

FieldTypeDefaultDescription
residenceIdstringrequiredId of the demand point where the pop lives; must exist.
jobIdstringrequiredId of the demand point where the pop works; must exist; may equal residenceId.
sizenumber200People the pop represents; finite, > 0 and at most 1000000.
drivingSecondsnumbernoneDrive time from residence to job; s; finite, >= 0 and at most 10000000; estimated as drivingDistance divided by 11.1 m/s with a floor of 60 s when omitted.
drivingDistancenumbernoneDrive distance from residence to job; m; finite, >= 0 and at most 50000000; estimated as straight-line distance times 1.3 when omitted.

AddPopsResult#

Result of demand.addPops.

FieldTypeDefaultDescription
successbooleanrequiredtrue when every pop was added.
popIdsstring[]noneIds of the new pops in input order; absent on failure.
errorstringnoneFailure reason, prefixed Entry <i>: for per-entry problems; absent on success.

UpdatePopInput#

Input for demand.updatePop. Every field is optional.

FieldTypeDefaultDescription
residenceIdstringnoneNew home point id; must exist; changing it re-estimates driving stats and clears commute data.
jobIdstringnoneNew work point id; must exist; changing it re-estimates driving stats and clears commute data.
sizenumbernoneNew people count; finite, > 0 and at most 1000000; both endpoints' counts move by the delta unless the unlink hit the 0 floor.

UpdatePopResult#

Result of demand.updatePop.

FieldTypeDefaultDescription
successbooleanrequiredtrue when the pop was updated.
errorstringnoneFailure reason; absent on success.

UpdatePopsEntry#

One entry for demand.updatePops: the UpdatePopInput fields plus the pop id.

FieldTypeDefaultDescription
idstringrequiredId of the pop to update; must be a string naming an existing pop.

UpdatePopsResult#

Result of demand.updatePops.

FieldTypeDefaultDescription
successbooleanrequiredtrue when every entry applied.
updatedCountnumbernoneNumber of entries applied, duplicates included; absent on failure.
errorstringnoneFailure reason, prefixed Entry <i>: for per-entry problems; absent on success.

RemovePopsResult#

Result of demand.removePops.

FieldTypeDefaultDescription
successbooleanrequiredtrue unless the input was invalid, the challenge block applied or an exception occurred.
removedCountnumbernonePops removed; unknown ids are skipped and not counted; absent on failure.
errorstringnoneFailure reason; absent on success.