Conversions
Conversion records in Siren's attribution pipeline — data model, status lifecycle, REST API, and PHP data access.
Last updated: April 9, 2026
Conversions
A conversion represents a tracked event where a collaborator’s engagement resulted in a measurable outcome — a sale, a lead capture, or a subscription renewal. Conversions sit between engagements (the upstream referral activity) and obligations (the downstream reward owed to the collaborator). They are the pivot point in Siren’s attribution pipeline: an engagement produces a conversion, and an approved conversion triggers an obligation.
Conversions are created by Siren’s event pipeline when an engagement is matched to a qualifying outcome. The incentive system evaluates the program’s rules, checks the engagement bindings, and creates conversion records automatically. The most common reasons to access conversion data directly are reading conversions for dashboards or reports, looking up conversions to make decisions in custom logic, and writing migration scripts that import historical data from another system.
The conversion object
| Field | REST name | PHP getter | Type | Description |
|---|---|---|---|---|
| ID | id | getId() | integer | Unique identifier |
| Engagement | engagementId | getEngagementId() | integer | The engagement that produced this conversion |
| Transaction | transactionId | getTransactionId() | integer or null | The associated transaction (e.g., an order), if any |
| Obligation | obligationId | getObligationId() | integer or null | The obligation created from this conversion, if any |
| Type | type | getType() | string | Conversion type identifier (e.g., sale, lead, renewal) |
| Status | status | getStatus() | string | Current status (see lifecycle below) |
| Created | dateCreated | getCreatedDate() | datetime | When the record was created |
| Modified | dateModified | getModifiedDate() | datetime | When the record was last updated |
The transactionId and obligationId fields are nullable because not every conversion starts with both. A conversion is created when the outcome is detected, but the obligation is only linked later once the incentive system calculates what the collaborator is owed. Similarly, some conversion types (like lead captures) may not involve a transaction at all.
Extended fields (REST only, via ?fields=)
These fields are resolved dynamically by the REST API field resolver system and must be explicitly requested.
| Field | Type | Description |
|---|---|---|
engagementScore | mixed | Score from the originating engagement |
engagementStatus | string | Status of the originating engagement |
programId | integer | ID of the program the conversion belongs to |
collaboratorId | integer | ID of the collaborator who earned this conversion |
collaboratorName | string | Display name of the collaborator |
transactionStatus | string | Status of the linked transaction |
transactionTotal | mixed | Total value of the linked transaction |
The ConversionType object
Conversion types define what kinds of events can be tracked as conversions. Each type declares which incentive structures support it.
| Field | Type | Description |
|---|---|---|
id | string | Unique type identifier (e.g., sale, lead) |
label | string | Plural display label |
singularLabel | string | Singular display label |
supportedIncentives | string[] | Array of incentive type IDs this conversion type supports |
Status lifecycle
| Status | Description |
|---|---|
pending | Initial state, awaiting review. Every conversion starts here. |
approved | The conversion is valid and triggers downstream obligation creation through the ConversionApproved event and MarkObligationsAsPending listener. This is the path that ultimately leads to a collaborator being rewarded. |
rejected | The conversion was denied. Fires the ConversionRejected event, which triggers RejectObligations to cancel any linked obligation. |
expired | The conversion fell outside the attribution window and is no longer valid. Expiration logic depends on program configuration. |
deleted | Soft-deleted. A second DELETE call permanently removes the record from the database. |
See How Refunds Work for a complete walkthrough of how refunds propagate through the system.
Accessing conversion data
# List pending conversions for a collaborator
curl -X GET "https://your-site.com/wp-json/siren/v1/conversions?status=pending&fields=id,type,status,collaboratorName" \
-H "Authorization: Bearer YOUR_TOKEN"
# Get a single conversion with extended fields
curl -X GET "https://your-site.com/wp-json/siren/v1/conversions/15?fields=id,type,status,programId,collaboratorName,transactionTotal" \
-H "Authorization: Bearer YOUR_TOKEN" use Siren\Conversions\Core\Datastores\Conversion\Interfaces\ConversionDatastore;
class ConversionReport
{
protected ConversionDatastore $conversions;
public function __construct(ConversionDatastore $conversions)
{
$this->conversions = $conversions;
}
public function getPendingConversions(): array
{
return $this->conversions->andWhere([
['column' => 'status', 'operator' => '=', 'value' => 'pending']
]);
}
} use Siren\Conversions\Core\Facades\Conversions;
$pending = Conversions::andWhere([
['column' => 'status', 'operator' => '=', 'value' => 'pending']
]);
$conversion = Conversions::getById(42); Siren is built on an event-driven architecture. Creating conversions manually via PHP bypasses the attribution system. The engagement won’t be validated, the program’s incentive rules won’t be evaluated, and downstream obligations won’t be created through the normal flow. If you need to trigger a conversion, fire the appropriate domain event instead of writing directly to this datastore.
PHP domain methods
The conversion datastore supports all shared methods documented in the introduction. There are no additional domain-specific methods beyond the standard CRUD operations.
Querying conversions for a transaction
A common read pattern is finding all conversions tied to a specific transaction. This is how Siren’s own listeners determine which conversions to approve or reject when a transaction completes or is refunded.
$conversions = $this->conversions->andWhere([
['column' => 'transactionId', 'operator' => '=', 'value' => $transactionId]
]);
Querying conversions by engagement
To find conversions that originated from a specific engagement:
$conversions = $this->conversions->andWhere([
['column' => 'engagementId', 'operator' => '=', 'value' => $engagementId]
]);
Counting pending conversions
$pendingCount = $this->conversions->countAndWhere([
['column' => 'status', 'operator' => '=', 'value' => 'pending']
]);
Relationships
Every conversion is linked to exactly one engagement via engagementId. The engagement sits upstream in the attribution pipeline and must exist at creation time, validated by the IdsExist middleware.
A conversion may optionally reference a transaction (such as an e-commerce order) via transactionId. This links the conversion to the concrete event it represents.
On the downstream side, approving a conversion causes the system to create an obligation recording what is owed to the collaborator. The obligationId back-reference is then stored on the conversion, closing the loop between the referral activity and the reward.
REST endpoints
See the individual endpoint pages in the sidebar for full request and response details, or browse the all REST endpoints reference.