Siren

Headless Collaborator Groups

Managing collaborator groups and binding programs and distributors to them over REST from a headless frontend. The binding is set with a collaboratorGroupId field on the program or distributor, not a separate endpoint.

Requires Siren Plus

Last updated: June 7, 2026

A collaborator group is a named cluster of collaborators with a structure (flat, linear chain, or parent-child). Once a program or distributor is bound to a group, the upline cascade and downline cascade calcs walk the group on every qualifying engagement.

The admin UI manages all of this from the WordPress dashboard. When your provisioning lives in code, a headless onboarding flow, an external CRM sync, or an infrastructure-as-code setup, you do the same work over REST. This page covers the workflow and the auth framing. For the wire-level request and response shapes of every endpoint, see Collaborator Groups (REST).

Who this applies to

This is a fully headless concern. Group management is not referral tracking that you bolt onto a WordPress storefront. It is administrative provisioning, so it always runs server-side against the admin REST surface, regardless of how the rest of your site is wired.

Authentication

Every collaborator-group endpoint is an admin endpoint. None of them are public, unlike the collaborator submission endpoint covered on the collaborator applications page. Call them from a server-side runtime (an Astro server function, a Next.js route handler, or a serverless function) using a WordPress application password. Treat that password as a server-side secret and never ship it to the browser.

Capabilities matter here. Full create, read, update, and delete on the CollaboratorGroup resource is granted only to the administrator and siren_platform_manager roles. Mint the application password from a user in one of those roles. A token from a lower-privilege role will be rejected by the capability check before the handler runs.

The two halves of the workflow

Wiring a group into your payout structure is two separate jobs. Keep them straight, because they live at different endpoints.

  1. Manage the group itself. Create the group, set its structure, and reconcile its members. All of this happens under the /collaborator-groups base.
  2. Bind a program or distributor to the group. This does not happen under /collaborator-groups at all. You set a field on the program or distributor. The next section is the part most people miss.

Binding is a field, not an endpoint

A program or distributor does not store its group association on its own row, and there is no /bind endpoint. The binding lives in the shared wp_siren_configs table. You set it by including a collaboratorGroupId field in the body of a PUT (or POST) against the program or distributor itself:

PUT /programs/{id}
{
  "collaboratorGroupId": 12
}

The distributor side is identical against its own resource:

PUT /distributors/{id}
{
  "collaboratorGroupId": 12
}

A listener on the program or distributor action event writes the config row and broadcasts a ProgramBoundToCollaboratorGroup or DistributorBoundToCollaboratorGroup event. This runs synchronously inside the same save, so the binding is in place by the time the request returns, and a read-back of the program or distributor reflects it. Passing null, "", or 0 clears the binding, which deletes the config row (a cleared binding is indistinguishable from one that was never set) and broadcasts the matching ProgramUnboundFromCollaboratorGroup or DistributorUnboundFromCollaboratorGroup event. Each program or distributor can be bound to at most one group. The full config-row keying is in the REST reference.

A typical provisioning round trip

Create the group

POST /collaborator-groups with name and structure. Optionally include the initial members in the same body. Siren returns the new group with its id.

Set the membership

Reconcile the roster with PUT /collaborator-groups/{id}/members, or add to it with POST. Each member's metadata carries its structure-specific data, like position for a linear chain.

Bind the program or distributor

PUT /programs/{id} or PUT /distributors/{id} with collaboratorGroupId set to the new group's id. This is the step that actually wires payouts to the group.

Cascades now fire

On the next qualifying engagement, the upline or downline cascade calc walks the bound group and credits peers per layer.

You can fold step 1 and step 2 together. POST /collaborator-groups accepts a members array in the create body, so a group and its full roster can be written in one request.

The ten endpoints at a glance

There are ten endpoints across the group surface. Nine manage the group and its members, one reads the structure registry, and the binding reuses the program and distributor endpoints you already have.

Group lifecycle:

  • GET /collaborator-groups lists groups. The fields query parameter is required.
  • GET /collaborator-groups/{id} reads one group.
  • POST /collaborator-groups creates a group, optionally with its initial members.
  • PUT /collaborator-groups/{id} updates name, description, or structure. Member changes do not go through this endpoint.
  • DELETE /collaborator-groups/{id} deletes the group and cascades its member rows. It does not detach bindings, so a program or distributor bound to the group keeps its collaboratorGroupId pointing at the now-deleted group, and its cascade then fails closed and credits no one. In a teardown flow, clear the binding first (a PUT against the program or distributor with collaboratorGroupId set to null), then delete the group.

Membership:

  • GET /collaborator-groups/{id}/members lists the members of one group.
  • POST /collaborator-groups/{id}/members adds members. Idempotent, so collaborators already in the group are skipped.
  • PUT /collaborator-groups/{id}/members full-replaces the roster, reconciling the group against the payload.
  • DELETE /collaborator-groups/{id}/members/{collaboratorId} removes one member.

Structure registry:

  • GET /collaborator-groups/structures lists the installed structure resolvers and the walker capabilities each one advertises.

The structures endpoint is worth one note for headless callers. Its providedWalkerCapabilities field is what the admin calc picker reads to hide calc strategies a bound group cannot support. The cascade calcs need the hasLayer capability, which linearChain and parentChild provide and flat does not. That filter is a frontend convenience. The REST API does not enforce it, so a client that sets a cascade calc against a flat-bound program will persist successfully and then emit no credits at runtime. Since the API does not validate it, a headless flow should check this itself: before binding a program that uses a cascade calc, read GET /collaborator-groups/structures, find the group’s structure, and confirm its providedWalkerCapabilities include hasLayer. The same hazard applies after the fact, because a PUT /collaborator-groups/{id} that switches a bound group to flat strips the capability and leaves the program’s saved cascade crediting no one. See Why some calculation methods disappear for the picker behavior.

Changing structure after the fact

PUT /collaborator-groups/{id} can change a group’s structure, and it broadcasts CollaboratorGroupStructureChanged when it does. Per-member metadata is not migrated when the structure changes. Switching a flat group to linearChain leaves every member without a position, which the linear-chain resolver treats as position 0. If your provisioning flips a group’s structure, follow it with a PUT /collaborator-groups/{id}/members that supplies the metadata the new structure needs.

See also