Customer map
The customer map places every customer of a project and environment on one two-dimensional map. Customers who behave alike sit close together, and the groups the model has found are drawn as areas. Customers close together behave alike; distances between far-apart areas, and the size of an area, are only approximate.
There is one map for each project and environment. It is drawn from the
embeddings of one deployment slot that the server chooses (or that an Admin
chooses), and it is read in one call, getCustomerMap.
Each time the map is updated, the server draws a new map and keeps the old one.
The API calls each of these maps an edition. An edition is a projection_2d
export of the slot, so its file is read like any other export,
with GET /slots/{slot}/exports/{export_id}, which mints the signed link.
The map reads the groups the model already has. An update never builds or changes groups. If a model has no group build yet, the map shows no groups until an Admin finds them in the console.
The current map
The map you see is the current map:
- While a pin is active, the pinned map.
- Otherwise the newest edition that is shown.
- Otherwise, while no shown edition exists, the newest finished map of the slot that carries group ids, else the newest finished map. A held map is never current.
A map can be held. A held map was kept back because it differs from the map before it by more than usual: it places far fewer customers than the map before it (and a smaller customer base does not explain it), or far fewer of its customers have a group. A held map is never current, and the previous map stays on screen. Admins see that a map was held and why.
Each new map is lined up with the current one, so customers stay close to where they were. When a new model replaces the old one, the map is drawn afresh and says so.
state in the answer says what is going on. Only one state is reported: the first
of these that applies.
| State | Meaning |
|---|---|
no_model | No slot qualifies yet, so there is no map. |
no_map | A slot qualifies, but no map has been drawn. |
first_map_pending | The first map is being drawn. |
pinned | An Admin pinned a map, so nothing replaces it until the pin ends. |
updating | A newer map is being drawn; the current map stays readable. |
failed | The newest update failed; last_failure names it. |
held | The newest update was held; the current map is the one before it. |
out_of_date | A scheduled update is overdue and has not started. |
paused | An Admin paused scheduled updates. |
on_request | Updates run only when someone presses Update now. |
needs_admin | No credential is available to run scheduled updates, or it may not use the slot's purpose. See Who runs an update. |
up_to_date | None of the above: the current map is the latest one. |
Which slot feeds the map
A slot can feed the map when it is active, has an active bundle, runs live, serves a sequence model and has a purpose that you may use. Without a choice, the server picks one: first a slot whose maps already carry group ids (a batch-only slot first), then a slot with a current group build, then a batch-only slot, then the newest activation. The choice is stored and kept while that slot still qualifies, so adding another slot never moves the map.
An Admin can choose the slot in the map settings. Update now and scheduled updates
never switch slot by themselves: if the chosen slot stops qualifying, Update now is
refused with the reason and scheduled updates fail where Admins can see them. Reads
do switch: while the chosen slot does not qualify, the map shows the slot the server
would choose for you, without its schedule or pin. The Admin's map settings still name the
chosen slot; Update now and saving the settings say why it no longer qualifies
(slot_not_eligible, or model_family_not_supported for an event model). The same holds for a stored slot that
you may not use: your reads show the automatic choice among the slots you may use.
?slot= shows another slot's map, read-only (403 permission_denied for a slot you
may not use).
When the map updates
Every project whose map has customer groups gets default settings: nightly at
03:00 UTC. A map with no standing credential to run as waits for an Admin
(needs_admin). An Admin can change the settings with
putCustomerMapSettings or in the console's
map settings.
frequency | Updates | Needs |
|---|---|---|
nightly | every day at local_time | none |
weekly | one day a week at local_time | weekday: 1 (Monday) to 7 (Sunday) |
monthly | one day a month at local_time | day_of_month: 1 to 28 |
custom | every every_hours hours | every_hours: 1 to 168, so never more often than hourly |
on_request | only when someone presses Update now | none |
- Time zone.
local_time(HH:MM, default03:00) is intime_zone, an IANA name such asEurope/London. The default isUTC. An Admin sets the zone; the console pre-fills the Admin's browser zone but saves it only when the Admin presses Save. Updates follow daylight-saving changes in the chosen zone. Host-local names (localtime,posix/…,right/…) are refused. - Pause.
paused: truestops scheduled updates and keeps the cadence. Update now still works while the map is paused or set toon_request. - One a day. For nightly, weekly and monthly maps, at most one scheduled update runs for each local date; a time that an Admin saves later the same day still runs that day.
- Next update.
next_run_atis the next scheduled update. It is null while the map is paused, pinned, on request, waiting for an Admin, or when the slot is one you may not use. - Cost controls. One update runs at a time for a map, and the cooldown, the daily cap and the pin below apply. Map updates meter nothing.
The settings are one document that you replace as a whole. Read it, change what you
need and send it back with the updated_at you read:
{
"slot_id": null,
"frequency": "weekly",
"local_time": "03:00",
"time_zone": "Europe/London",
"weekday": 1,
"paused": false,
"cooldown_minutes": 15,
"pin": null,
"expected_updated_at": null
}
expected_updated_at guards against two Admins saving at once. If the settings
changed since you read them, the save is 400 failed_precondition with reason
version_mismatch and nothing changes. Use null while no settings are stored
yet. Until then a read returns the defaults, with created_at and updated_at
null, and writes nothing. With no stored settings, runs_as is none and next_run_at
is null, unless the project and environment has a succeeded map with customer groups;
then they show the nightly default and, when a standing credential requested that
map, who it would run as.
Update now
Update now (refreshCustomerMap) starts a new map straight away. Members and
Admins can use it; Viewers cannot. An API key needs the admin scope, and
applications are refused.
It takes no body. Every update uses the same fixed recipe: every customer up to 200,000, the same seed, and the map's own colours. Nobody chooses columns, sample size or seed, which is why a Member may use it when creating an export stays with Admins.
The call runs these checks in order, and the first that fails answers:
- Permission. Role or key scope.
- The map's slot. The slot that is stored (for an automatic map, chosen again by
the server's rule if it stopped qualifying), never a slot picked from what you may
use. A chosen slot that no longer qualifies is
400 failed_precondition(slot_not_eligible, ormodel_family_not_supportedfor an event model); no slot at all isno_model. - Purpose. A caller who may not use the slot's purpose gets
403 permission_denied(purpose_not_allowed) and learns nothing else about the map. - Pin.
map_pinned, withuntil. - One at a time. While an update is queued or running, the call starts
nothing and answers
202with that update. An update still running on the slot the map followed before a slot change isupdate_in_progress, withretry_after. - Groups.
groups_missingwhile the newest update was held because groups were missing and the slot's group build has not changed since. - Cooldown.
cooldown, withretry_after. - Daily cap.
daily_cap, withretry_after. - The update starts.
202with the new edition.
The limits depend on who calls:
| Admin session | Anyone else | |
|---|---|---|
| Cooldown after a successful update | the map's cooldown_minutes (0 to 1,440; default 15) | the larger of cooldown_minutes and 15 minutes |
| After a failed update | may retry at once, but never inside the cooldown after a success | waits a backoff of min(effective cooldown, 2^n) minutes after n failures in a row, and never less than the cooldown after the last success |
| Updates started in 24 hours, for each map | no cap | 24 |
An Admin caller is a console session that holds customer_map.settings.put. Every
API key counts as anyone else. A cooldown of 0 therefore applies to Admins only.
Several people pressing the button at once start one update: the others get the
update already running, or, for a moment, 503 update_in_progress with a
Retry-After to try again. An Idempotency-Key replays the first answer for
24 hours.
An update runs as the person (or key) who pressed Update now, so the job uses that caller's purposes. It is never run without a credential.
Pin and history
An Admin can pin one map until a date. While the pin is active the pinned map
is the current map, scheduled updates are skipped and Update now is refused with
map_pinned. When the date passes, updates resume by themselves.
pin.untilmust be after now and at most 90 days ahead.pin.export_idnames a succeededprojection_2dexport of the slot the map follows, coloured only by scores and group ids. Leave it out to pin the current map. Any other export of the project (another slot, one that did not succeed, one that is notprojection_2d, or one coloured by event attributes) is400 failed_preconditionpin_export_not_eligible, so Members and Viewers never see such a column on the map.- An export that does not exist, or belongs to another project or environment, is
404 not_found. With no current map to pin the answer is400 no_map. - Changing the slot while a pin on another slot is active is refused
(
pin_other_slot). Remove the pin in the same request, or first. pin: nullremoves the pin.
The history (listCustomerMapEditions) lists the editions of the map's current
slot, newest first, 50 at most per page, with cursor paging. Each entry gives the
edition, what changed against the edition before it, and its layout and stability
figures. Held editions are listed with verdict: held. The pinned map is listed
too when it is not itself an edition, in date order and counted within the page
limit. Editions of a slot the map no longer follows are not listed. An edition
drawn before editions recorded a verdict or a layout reads not_recorded.
Who runs an update
- Update now runs as the caller.
- Scheduled updates run as the Admin session, or the API key with the
adminscope, that last saved the map settings (runs_as: admin). Before the first save, the settings take the requester of the newest successful map of the same slot, if that is a standing user or API key of the environment (map_requester). An application never qualifies. - The stored identity is checked again before every scheduled update. A person who
was removed, a key that was revoked, a saver who no longer holds
customer_map.settings.put, or a credential that may not use the slot's purpose no longer qualifies. - With no credential that qualifies,
runs_asisnone, no scheduled update runs until an Admin saves the settings, and the state readsneeds_adminunless an earlier state in the table applies. Nothing ever runs without a credential.
Permissions
The map has five permissions: three reads for everyone who can read the project, Update now for Members, and settings for Admins. The table, the built-in floors, the API key scopes and the extra limits on Update now are in Custom roles and SCIM provisioning. A custom role made before these permissions existed does not hold them.
Reads show only what the caller's purposes allow: a slot the caller may not use is
left out, not refused, and so are its pin and next update time (naming that slot with
?slot= is refused). Started and joined updates, settings changes, and refusals for
role, key scope or purpose are audited as customer_map.refresh and
customer_map.settings.update.
Metering
Map editions meter nothing. A scheduled update and an Update now emit no
export_rows usage and never appear in usage. A projection_2d export that
someone requests by hand with POST /slots/{slot}/exports still meters as before.
Webhooks
Every map update publishes export.completed or export.failed, like any
projection_2d export, and the event's data.kind is projection_2d. A receiver
that wants only maps filters on data.kind. To tell an update from a projection
someone requested by hand, look in a succeeded event's data.metadata: updates
carry layout, verdict and alignment, and a manual projection carries none of
them. See webhooks for delivery and signing.
The five operations
| Operation | Path | Who | What it does |
|---|---|---|---|
getCustomerMap | GET /customer-map | Viewer | The current map, its state, the schedule and the pin. |
refreshCustomerMap | POST /customer-map:refresh | Member | Update now. Takes no body; accepts Idempotency-Key. |
getCustomerMapSettings | GET /customer-map/settings | Viewer | The schedule, time zone, cooldown, pin and whose credential runs updates. |
putCustomerMapSettings | PUT /customer-map/settings | Admin | Replaces the settings and the map's schedule together. |
listCustomerMapEditions | GET /customer-map/editions | Viewer | The history of the map. |
All five are described in the customer map API reference.
The map's schedule also appears in GET /schedules as a row of kind
customer_map; see schedules.