For the complete documentation index, see llms.txt. This page is also available as Markdown.

Members Bulk Remove

Overview

Reference for the bulk member-removal endpoint, which removes product access for many members in one call, identifying each member by email address. Use alongside the OpenAPI spec for full request/response schemas.

This is the counterpart to POST /members/import-csv (see Members CSV Import). The import endpoint creates and updates members from a CSV; this endpoint removes product access for members that already exist. It takes a JSON body — there is no CSV form of bulk removal.

Endpoint

Method
Path
Purpose

DELETE

/members/bulk-remove

Removes access to one product for the listed members. Only members whose role for that product is MEMBER are affected; admins are skipped. Returns the number of members removed.

Content type is application/json. Unlike import-csv, the product is supplied in the request body rather than as a query parameter.

Authorization

  • product=PERFORM requires the Perform Admin role.

  • All other products require the Admin role.

  • Authentication is the same scheme as /members/import-csv.

The endpoint is additionally gated by the user-list-members-bulk-delete feature flag. If the flag is not enabled for your org, every call returns 403 Forbidden and nothing is removed. Contact your Flo representative to have it enabled.

Request body

{
  "product": "RECRUIT",
  "emails": ["jane.smith@firm.com", "john.doe@firm.com"]
}
Field
Type
Required
Notes

product

enum

Yes

One of RECRUIT, PERFORM, UNI. The product whose access is being removed. Determines the required admin role (see Authorization).

emails

array of string

No

Email addresses of the members to remove. Omitting the field, or sending null or [], is a valid no-op that returns removedCount: 0.

There is no enforced cap on the number of emails, but the whole call runs in a single transaction — prefer batches of a few thousand over one very large request.

Matching semantics

  • Members are matched by email address, against the email stored on the member record in your org. Members in other orgs are never visible to the call.

  • The API performs no normalization on the values you send — no trimming, no case folding, no alias resolution. Send the exact email on file.

  • An email that matches nothing is silently ignored. It is not an error, and it does not appear in the response; it simply doesn't contribute to removedCount. A call whose every email is unknown returns 200 OK with removedCount: 0.

  • Duplicate emails in the array are harmless — each member is removed at most once, so removedCount counts members, not array entries.

What gets removed — and what doesn't

The operation removes the member's access to the one product named in the request. It is not a member deletion.

Effect

The member's access to the requested product

Removed

The member's access to other products

Preserved

The member's user record, profile, and employment data

Preserved

Members whose role for the requested product is ADMIN

Skipped — not removed, not reported as an error

Because admins are skipped rather than rejected, including an admin's email in the list is safe: the rest of the batch still processes, and the admin's access is left intact. The only signal is that removedCount comes back lower than the number of emails you sent.

To remove an admin, first demote them to MEMBER (via import-csv with a Role column of MEMBER, or the editEmployerMemberAuthInfo GraphQL mutation), then re-run the bulk remove. To delete a member entirely rather than revoking one product, use the deleteMember GraphQL mutation — see Alternative: GraphQL mutations below.

Success response

200 OK with:

removedCount is the number of members whose product access was actually removed. It excludes skipped admins and unmatched emails, so it is the authoritative signal of what happened — always compare it against the number of emails you sent.

Error responses

Status
Meaning

401 Unauthorized

The request is unauthenticated.

403 Forbidden

Either the caller lacks the required role for the product (Perform Admin for PERFORM, Admin otherwise), or the user-list-members-bulk-delete feature flag is not enabled for your org.

There is no row-level error response and no partial-failure body — unlike import-csv, this endpoint has nothing to validate per email. An email is either matched (and removed) or ignored. The call is a single transaction: on any failure nothing is removed.

  1. Determine the product you are removing access from, and confirm you hold the matching admin role.

  2. Collect the exact email addresses on file for the members to remove.

  3. DELETE /members/bulk-remove with the JSON body.

  4. On 200 OK, compare removedCount against the number of emails sent. A shortfall means one or more emails didn't match a member, or matched an admin.

  5. If you need to know which emails didn't take effect, re-query the members (membersByProduct / memberById in GraphQL) and diff against your list — the response body deliberately carries only a count.

Recipes

Recipe 1: offboard a batch of members from Recruit

Each of the three loses Recruit access. Any of them that is a Recruit admin, or whose email doesn't match a member, is skipped.

Recipe 2: remove Perform access but keep Recruit access

Requires the Perform Admin role. Jane keeps her Recruit access and her member record; only the Perform role is removed.

Recipe 3: dry-run-ish validation before a large batch

There is no dry-run mode. Send a single-email request for one representative member first, confirm removedCount is 1, then submit the rest of the batch.

Things to be careful about

  • This is not a member deletion. The member stays in your org with their other product access intact.

  • Admins are skipped silently. No error, no per-email report. Watch removedCount.

  • Email matching is exact and unnormalized. A trailing space, a different alias, or a stale address on your side produces a silent no-op for that entry.

  • Some HTTP clients drop request bodies on DELETE. A few libraries and proxies strip the body from a DELETE, which arrives as an empty email list and returns removedCount: 0. If you get an unexplained zero, confirm your client actually sent the body.

  • There is no dry-run mode and no undo. Re-granting access requires import-csv, and re-granting fires a welcome email.

  • The feature flag gate is per-org. A call that works for one org can return 403 for another if the flag isn't enabled there.

Example: curl

Response:

Common pitfalls

Symptom
Likely cause

removedCount is lower than the number of emails sent

One or more emails matched no member in your org, or matched a member whose role for that product is ADMIN (admins are skipped).

removedCount: 0 on a request you know is correct

The request body didn't arrive — some HTTP clients strip bodies from DELETE. Verify with a raw curl.

403 Forbidden on every call

The user-list-members-bulk-delete flag isn't enabled for your org, or you're targeting PERFORM without the Perform Admin role.

An admin wasn't removed

By design. Demote to MEMBER first, then re-run.

The member still appears in your other integrations

Only the one product's access was removed. The member record itself still exists — use deleteMember for a full deletion.

Member removed from the wrong product

product is in the request body, not a query parameter as it is on import-csv. Double-check the body field.

Last updated