Corteksa

Bulk assign — multi-assignee

Bulk assign — multi-assignee

POST /api/v1/object/data/{objectSlug}/bulk-assign used to take exactly one owner (admin_slug). It now takes the same assignee set as the per-record endpoint, so the list/table bulk action can assign several people to many records in one call.

Nothing breaks: admin_slug still works and still means "these records belong to this one person".


1. The new body

POST /api/v1/object/data/leads/bulk-assign
{
  "slugs": ["lead-1", "lead-2", "lead-3"],   // records to change (max 500)
  "assignees": ["sam-a1b2", "ann-c3d4"]      // desired FULL set (max 20)
}
BodyMeaning
assignees: ["sam", "ann"]Every selected record ends up with exactly Sam + Ann
assignees: []Unassign every selected record
admin_slug: "sam"Legacy single-owner form — identical to assignees: ["sam"]
admin_slug: null / omittedUnassign (unchanged behavior)
both keys together400 Send either admin_slug or assignees, not both

Semantics are set-replace, the same rule as PATCH /object/data/{objectSlug}/{recordSlug}/assignees: the request states the final set, not a delta. There is no add/remove mode — to add someone to records that already have assignees, send the union you want them to end up with.

Who ends up as primary owner

The primary owner is stable unless you replace them. One rule, identical on both endpoints:

Record beforeYou sendOwner after
Owned by Sam, co-assignee Bob["ann","sam"]Sam — still in the set, so ownership doesn't move
Owned by Bob["ann","sam"]Ann — Bob was dropped, so your first pick takes it
Unassigned["ann","sam"]Ann — first pick
Owned by Sam[]nobody — unassigned

So a bulk assign over a mixed selection can leave different owners on different records: every record that already had one of the chosen people as owner keeps them. This is deliberate — reassigning a record's owner is a separate intent from adding people to it. If you need "make Ann the owner of all of these", send ["ann"] (or admin_slug: "ann"), which drops the previous owner and therefore hands every record to Ann.

Ownership drives the kanban lane, the notification target, the export owner column, and assignees[0] on every read.

Response is unchanged:

{ "message": "Bulk assign completed", "data": { "updatedCount": 3 } }

updatedCount counts the records that were located and passed the access check — records the caller cannot edit, or slugs that no longer exist, are skipped silently, as before.

2. Errors the picker must handle

Validation is now shared with the per-record endpoint, so the same failures return the same shapes on both:

CaseStatusBody
Slug unknown, or an admin from another workspace422{ "code": "INVALID_ASSIGNEE", "reason": "unknown_or_foreign_workspace_admin" }
More than 20 assignees422{ "code": "TOO_MANY_ASSIGNEES", "max": 20 }
More than 500 record slugs422class-validator array-size error
Both admin_slug and assignees sent400Send either admin_slug or assignees, not both

Changed: an unknown admin_slug used to return 400 Admin with slug 'x' not found. It now returns the 422 INVALID_ASSIGNEE above. Nothing else about the legacy form changed.

The whole request is rejected — never a partial set, and never a partial selection.

3. Permission

Unchanged: the endpoint needs assign.{objectSlug} (the dedicated assign right), and every selected record is additionally row-scoped by the caller's edit access. A user with assign on the object but only M-level records can still bulk-assign only their own records.

4. What the UI should do

  • Reuse the same multi-select picker as the record drawer's assignee field; send its full value as assignees.
  • Pre-fill the picker with the current set when the selection is homogeneous, and leave it empty otherwise — because this is set-replace, an empty picker submitted as assignees: [] unassigns everyone.
  • Warn before assigning over records that already have co-assignees: the set is replaced, not merged. (This was already true of the old single-owner call, which silently dropped co-assignees.)
  • Don't promise the user "X will own these" in the confirmation copy unless the set you send is a single person — with a multi-person set, records that already belong to one of the chosen people keep their current owner.
  • After the call, refetch the affected rows — assignees[] is returned on every list/search read, so the avatar chips update without opening a record.

5. Not included

  • No add/remove mode. Only replace. If "add X to the selection without touching the rest" turns out to be the common gesture, say so and it becomes a mode flag on this endpoint.
  • No assignment notifications on bulk. The per-record PATCH …/assignees notifies each newly-added assignee; bulk stays silent (unchanged), because a 500-record × 20-assignee call would fan out 10 000 notifications. The audit changelog and webhooks still fire per record.
  • The AI bulk-assign tool still takes one assignee. bulk_assign_records keeps its single assignee_slug argument; only the REST endpoint takes a set.

On this page