Siren

Program Groups

Program groups in Siren — data model, sorting strategies, REST API, and PHP data access for program priority resolution.

Last updated: April 9, 2026

Program Groups

A program group collects one or more programs under a shared sorting strategy. When a collaborator qualifies for multiple programs within the same group, the sorter determines which program takes priority. This lets you set up layered incentive structures. For example, a seasonal bonus program can override the default commission without worrying about double-counting.

Each group has a sorter strategy that controls how priority is resolved. The built-in strategies are oldestBindingWins (the program with the earliest engagement takes priority) and newestBindingWins (the most recently engaged program wins). The relationship between programs and groups is managed through a separate junction datastore in PHP and via extended fields in REST.

Program groups do not carry a status field, so there is no soft-delete lifecycle. Deletes are always permanent.

The program group object

FieldREST namePHP getterTypeDescription
IDidgetId()integerUnique identifier
NamenamegetName()stringDisplay name of the group
DescriptiondescriptiongetDescription()stringHuman-readable description of the group’s purpose
SortersortergetSorter()stringSorting strategy identifier (see below)

Extended fields (REST only, via ?fields=)

These fields are resolved dynamically by the REST API field resolver system and must be explicitly requested.

FieldTypeDescription
programIdsinteger[]Array of program IDs assigned to this group
programsobject[]Array of program objects with id, name, and status for each program in the group

Sorting strategies

The sorter field controls how the system resolves priority when a collaborator matches multiple programs in the same group.

StrategyBehavior
oldestBindingWinsThe program the collaborator was bound to first takes priority
newestBindingWinsThe most recently bound program takes priority

Accessing program group data

# List all program groups
curl -X GET "https://your-site.com/wp-json/siren/v1/program-groups?fields=id,name,sorter,programIds" \
  -H "Authorization: Bearer YOUR_TOKEN"

# Get a single program group with full program details
curl -X GET "https://your-site.com/wp-json/siren/v1/program-groups/5?fields=id,name,description,sorter,programs" \
  -H "Authorization: Bearer YOUR_TOKEN"
use Siren\ProgramGroups\Core\Datastores\ProgramGroup\Interfaces\ProgramGroupDatastore;

class GroupManager
{
    protected ProgramGroupDatastore $groups;

    public function __construct(ProgramGroupDatastore $groups)
    {
        $this->groups = $groups;
    }

    public function getGroup(int $groupId): \Siren\ProgramGroups\Core\Models\ProgramGroup
    {
        return $this->groups->getById($groupId);
    }
}
use Siren\ProgramGroups\Core\Facades\ProgramGroups;

$group = ProgramGroups::getById(5);

$allGroups = ProgramGroups::andWhere([]);

Siren is built on an event-driven architecture. Program groups are normally configured through the admin UI. If you need to create them programmatically (e.g., during a migration or recipe import), the datastores documented here are the right tool. The group’s sorting strategy is applied automatically during the attribution pipeline. You don’t need to invoke it manually.

PHP domain methods

The program-group junction

The relationship between programs and groups is tracked through a junction datastore. Each junction record links one program to one group. A program can belong to at most one group.

The junction model has two fields:

FieldPHP getterTypeDescription
Program IDgetProgramId()integerThe program’s primary key (also serves as the junction’s identity)
Group IDgetGroupId()integerThe group this program belongs to
use Siren\ProgramGroups\Core\Datastores\ProgramsProgramGroups\Interfaces\ProgramsProgramGroupsDatastore;

class GroupInspector
{
    protected ProgramsProgramGroupsDatastore $junctions;

    public function __construct(ProgramsProgramGroupsDatastore $junctions)
    {
        $this->junctions = $junctions;
    }

    public function getProgramsInGroup(int $groupId): array
    {
        return $this->junctions->getProgramsForGroup($groupId);
    }

    public function findGroupForProgram(int $programId): ?\Siren\ProgramGroups\Core\Models\ProgramGroup
    {
        return $this->junctions->getProgramGroupForProgram($programId);
    }
}
use Siren\ProgramGroups\Core\Facades\ProgramsProgramGroups;

// Get all programs in a group
$programs = ProgramsProgramGroups::getProgramsForGroup($groupId);

// Find which group a program belongs to (returns null if ungrouped)
$group = ProgramsProgramGroups::getProgramGroupForProgram($programId);

Looking up programs in a group

getProgramsForGroup retrieves all program models that belong to a specific group. This crosses the junction boundary. It accepts a group ID but returns full Program model instances, not junction records.

use Siren\ProgramGroups\Core\Facades\ProgramsProgramGroups;

$programs = ProgramsProgramGroups::getProgramsForGroup($groupId);

foreach ($programs as $program) {
    echo $program->getName() . ' (' . $program->getIncentiveType() . ')';
}

Finding a program’s group

getProgramGroupForProgram looks up the group a program belongs to. Returns null if the program is not in any group. This is useful for checking whether attribution deduplication applies to a given program.

use Siren\ProgramGroups\Core\Facades\ProgramsProgramGroups;

$group = ProgramsProgramGroups::getProgramGroupForProgram($programId);

if ($group !== null) {
    echo $program->getName() . ' is in group: ' . $group->getName();
    echo 'Priority strategy: ' . $group->getSorter();
} else {
    echo 'This program is not in a group — no deduplication applies.';
}

Relationships

  • Programs. A program group contains zero or more programs via a junction table. Each program can belong to at most one group (the programId is the primary key on the junction table). The programs and programIds extended fields expose these associations on read endpoints.
  • Sorting and Attribution. When a collaborator qualifies for multiple programs within the same group, the group’s sorter strategy determines which program takes precedence. This affects which incentive structure applies to a given conversion.

REST endpoints

See the individual endpoint pages in the sidebar for full request and response details, or browse the all REST endpoints reference.