Siren

Opportunities

Accessing and querying Siren opportunity records through the PHP data layer.

Last updated: April 8, 2026

Opportunities

An opportunity in Siren is an ephemeral tracking record created automatically when a visitor clicks an affiliate link or otherwise triggers an attribution-worthy interaction. Opportunities represent the earliest stage of the attribution pipeline. A potential customer touchpoint may eventually lead to an engagement, conversion, and transaction.

You will almost never create opportunities manually. They are produced by Siren’s trigger strategies in response to site visits, cookie lookups, and other detection mechanisms. The opportunity system handles creation, deduplication, merging, and invalidation through the normal event flow. The primary reason to access the opportunity datastore directly is reading opportunity data for reporting, looking up an opportunity to inspect its status, or tracing the attribution chain backward from a conversion.

Siren is built on an event-driven architecture. Opportunities are created and managed by trigger strategies that respond to visitor activity events. If you need to support a new type of visitor interaction, register a custom trigger strategy rather than creating opportunity records directly. This ensures the full event pipeline fires and downstream systems (engagements, conversions) respond correctly.

Accessing opportunity data

The opportunity datastore is available through dependency injection or the static facade. Dependency injection is preferred for extension code wired through an initializer. The facade is convenient for standalone scripts, theme files, or other code running outside Siren’s container.

use Siren\Opportunities\Core\Datastores\Opportunity\Interfaces\OpportunityDatastore;

class AttributionTracer
{
    protected OpportunityDatastore $opportunities;

    public function __construct(OpportunityDatastore $opportunities)
    {
        $this->opportunities = $opportunities;
    }

    public function getActiveOpportunities(): array
    {
        return $this->opportunities->andWhere([
            ['column' => 'status', 'operator' => '=', 'value' => 'active']
        ]);
    }
}
use Siren\Opportunities\Core\Facades\Opportunities;

$active = Opportunities::andWhere([
    ['column' => 'status', 'operator' => '=', 'value' => 'active']
]);

$opportunity = Opportunities::getById(42);

The opportunity model

Each opportunity record is represented by an Opportunity model instance.

FieldTypeDescription
idintPrimary key
statusstringactive or invalid
lastTriggeredDateTimeWhen the opportunity was most recently triggered
createdDateDateTimeWhen the opportunity was first recorded
modifiedDateDateTimeWhen the record was last updated

Getter methods: getStatus(), getLastTriggered(), getCreatedDate(), getModifiedDate().

The opportunity model is deliberately lightweight. It does not carry collaborator or program information. Those associations are established by engagements, which link an opportunity to a collaborator within a program. A single opportunity can have multiple engagements across different programs if the visitor’s interaction triggers attribution in more than one context.

The lastTriggered timestamp updates each time the visitor re-triggers the opportunity (for example, a return visit through the same affiliate link). This allows Siren to track recency without creating duplicate records.

Available methods

This datastore supports all shared methods documented in the introduction. It also provides one domain-specific method.

Looking up an opportunity by conversion

getByConversionId retrieves the opportunity that led to a specific conversion. This is the primary method for tracing the attribution chain backward. Given a conversion, you can find the original opportunity, and from there query the engagements that were active when the conversion occurred.

use Siren\Opportunities\Core\Datastores\Opportunity\Interfaces\OpportunityDatastore;
use PHPNomad\Datastore\Exceptions\RecordNotFoundException;

class ConversionTracer
{
    protected OpportunityDatastore $opportunities;

    public function __construct(OpportunityDatastore $opportunities)
    {
        $this->opportunities = $opportunities;
    }

    public function traceConversion(int $conversionId): ?array
    {
        try {
            $opportunity = $this->opportunities->getByConversionId($conversionId);

            return [
                'opportunityId' => $opportunity->getId(),
                'status' => $opportunity->getStatus(),
                'firstSeen' => $opportunity->getCreatedDate(),
                'lastTriggered' => $opportunity->getLastTriggered(),
            ];
        } catch (RecordNotFoundException $e) {
            return null;
        }
    }
}
use Siren\Opportunities\Core\Facades\Opportunities;

// Trace back from a conversion to its originating opportunity
$opportunity = Opportunities::getByConversionId($conversionId);

echo $opportunity->getId();            // The opportunity that led to this conversion
echo $opportunity->getLastTriggered(); // When the visitor last interacted

Throws RecordNotFoundException if no opportunity is associated with the given conversion ID.

This method is only available on the datastore interface, not on the facade’s docblock hints, but it is callable through the facade since the facade delegates all method calls to the underlying datastore instance. If your IDE does not autocomplete it through the facade, you can access it via Opportunities::getContainedInstance()->getByConversionId($conversionId) for full type safety.