Bulk upsert member locations
## Organization Locations - Bulk Upsert
Organization Locations - Bulk Upsert
Add or update sites on many members' accounts in one call.
Each location is applied to each target member, matched on external_id. A new external_id
creates the location, an existing one updates it. Sending the same payload again returns
unchanged for every row and writes nothing.
Location fields
| Field | Required | Notes |
|---|---|---|
external_id | yes | Your id for the site, up to 100 characters. Unique per user. |
name | yes | Up to 50 characters |
latitude, longitude | yes | Decimal degrees, stored to 5 decimal places |
warning_radius | no | Miles, 3 to 12. Default 8 on create |
all_clear_countdown | no | Minutes, 0 to 60 |
notifications | no | Lightning notifications. Default true on create |
hazards | no | Per-hazard switches, e.g. {"wbgt": {"enabled": false}}. Hazards left out use the defaults, the same as a location the user created |
On an update, optional fields you leave out are not changed. The locations are ordinary locations on each user's account: they appear in the user's dashboard and app and alert the same way. A user's default location is never changed.
Targets
targets picks the members to apply the change to. Give exactly one of:
| Key | Matches |
|---|---|
all_members | true: every member |
user_ids | FLASH user ids |
emails | Member email addresses, case-insensitive |
member_external_ids | Your own member ids, set with PATCH /organization/members/{user_id}/ |
tags | Members carrying any of the tags, set with PATCH /organization/members/tags/ |
Members whose invitation is still pending are included. Their locations are saved but do not
alert until they accept, and each of their rows carries the warning user_inactive.
Limits
- Up to 1000 result rows per request (targets x locations). Split larger jobs into several
requests. A larger request is refused with
BATCH_TOO_LARGEand nothing is written. - Up to 30 locations per user. A create past that is an error row with code
location_limit.
Results
The response has one row per target and location, in request order, plus a summary count.
Each member's changes are written together. Rows for one member never depend on another
member's.
status | Meaning |
|---|---|
created | New location on the user's account |
updated | Existing location changed |
unchanged | Already matched, nothing written |
deleted | Location removed |
skipped_user_deleted | The member deleted this location, so it was not recreated |
error | Nothing written for this row. See code |
Error codes: not_a_member, location_limit, not_found, conflict (another request was
changing the same user's locations; retry).
Warnings do not stop the write:
org_policy_applied:warning_radius/org_policy_applied:all_clear_countdown: the organization's setting replaced the value you sent.notifications_outside_coverage: the site is outside the lightning coverage area, so its notifications are off.user_inactive: the member has not accepted their invitation yet.kept_user_edit:<field>: the member changed this field themselves, so their value was kept.restored_user_deleted: a location the member deleted was recreated because you sentrestore_user_deleted.
Member edits and deletions
Members can rename, move, switch notifications on or off, and delete locations you manage. Later calls respect that:
- A location the member deleted is not recreated. Its row comes back as
skipped_user_deleted. Sendrestore_user_deleted: trueto recreate it anyway. - Fields the member changed (
name,latitude,longitude,notifications) keep the member's value, with akept_user_edit:<field>warning. Sendoverride_user_edits: trueto write yours and reset that tracking.
dry_run: true returns exactly what the call would do and writes nothing.
A request that fails validation is refused as a whole with INVALID_REQUEST, and details
lists every problem by field and index. Nothing is written.
Authentication
Two ways to call these endpoints, both scoped to one organization:
- Secret API key in the
X-Flash-Api-Keyheader (fw_sk_...). The organization needs the Organization Locations API product. The key always acts on its own organization. Publishable keys are refused. This is the mode for software partners integrating on an organization's behalf. - User token (
Authorization: Token ...) of an administrator of the organization.
Either way, only members of that organization can be read or changed. A user outside it is
reported as not_a_member, whether or not the account exists.
FLASH API key. Secret keys are prefixed fw_sk_live_ and must stay server-side; publishable keys are prefixed fw_pk_live_ and are safe in a public page. Obtain via POST /api-keys/.
In: header
Request Body
application/json
TypeScript Definitions
Use the request body type in TypeScript.
Rejects keys it does not declare, so a misspelt field fails instead of being ignored.
Response Body
application/json
application/json
application/json
application/json
application/json
curl -X POST "https://example.com/organization/locations/bulk-upsert/" \ -H "Content-Type: application/json" \ -d '{ "targets": { "tags": [ "crew-north" ] }, "locations": [ { "external_id": "crowther-site-118", "name": "Lakeview Dr re-roof", "latitude": 41.88, "longitude": -87.63, "warning_radius": 8, "all_clear_countdown": 30, "notifications": true }, { "external_id": "crowther-site-121", "name": "Oak St warehouse", "latitude": 41.9, "longitude": -87.68, "hazards": { "wbgt": { "enabled": false } } } ], "dry_run": false }'{ "organization_id": 42, "dry_run": false, "summary": { "created": 2, "updated": 0, "unchanged": 1, "deleted": 0, "skipped_user_deleted": 0, "error": 1 }, "results": [ { "user_id": 311, "email": "dana@crowther.example", "member_external_id": "crowther-emp-17", "external_id": "crowther-site-118", "location_id": 90211, "status": "created", "warnings": [ "org_policy_applied:warning_radius" ] }, { "user_id": 311, "email": "dana@crowther.example", "member_external_id": "crowther-emp-17", "external_id": "crowther-site-121", "location_id": 90212, "status": "created" }, { "user_id": 318, "email": "lee@crowther.example", "member_external_id": null, "external_id": "crowther-site-118", "location_id": 88001, "status": "unchanged" }, { "user_id": 318, "email": "lee@crowther.example", "member_external_id": null, "external_id": "crowther-site-121", "location_id": null, "status": "error", "code": "location_limit", "detail": "This user already has the maximum of 30 locations." } ]}
