Obligations
Payment obligations owed to collaborators — data model, status lifecycle, REST API, and PHP data access.
Last updated: April 9, 2026
Obligations
An obligation represents a debt the business owes a collaborator as a result of an approved conversion. Obligations sit downstream of conversions and upstream of fulfillments. They are the “IOU” in Siren’s attribution pipeline. When a conversion is approved, the incentive system evaluates the program’s rules and creates an obligation for the calculated reward amount. Once obligations are gathered into a fulfillment batch and paid out, their status transitions to fulfilled.
The obligation object
| Field | REST name | PHP getter | Type | Description |
|---|---|---|---|---|
| ID | id | getId() | integer | Unique identifier |
| Collaborator | collaboratorId | getCollaboratorId() | integer | The collaborator this obligation is owed to |
| Status | status | getStatus() | string | Current status (see lifecycle below) |
| Award type | awardType | getAwardType() | string | How the reward was calculated (e.g., commission, flat) |
| Value | value | getValue() | integer | Reward amount in the smallest currency unit (e.g., cents) |
| Payout | payoutId | getPayoutId() | integer or null | The payout this obligation was fulfilled through, if any |
| Created | dateCreated | getCreatedDate() | datetime | When the record was created |
| Modified | dateModified | getModifiedDate() | datetime | When the record was last updated |
The value field stores amounts as integers in the smallest currency unit. A $15.00 commission is stored as 1500. This avoids floating-point precision issues in financial calculations.
The payoutId is null until the obligation is included in a payout batch. Once a payout is processed and the obligation is marked fulfilled, the payoutId links it to the payout record that settled the debt.
Extended fields (REST only, via ?fields=)
These fields are resolved dynamically by the REST API field resolver system and must be explicitly requested.
List endpoint (GET /obligations): collaboratorName, collaboratorEmail
Detail endpoint (GET /obligations/{id}): collaboratorName, collaboratorEmail, conversionId, conversionType, conversionDate, conversionAmount, programId, programName
Status lifecycle
Draft
A conversion is recorded but not yet approved. The obligation exists but is not actionable.
Pending
The conversion has been approved and the obligation is awaiting fulfillment.
Fulfilled
Included in a fulfillment batch and linked to a payout record.
| Status | Description |
|---|---|
draft | Initial state when a conversion is recorded but not yet approved. The obligation exists but is not actionable. |
pending | The conversion has been approved and the obligation is awaiting fulfillment. |
fulfilled | Included in a fulfillment batch. The payoutId field links it to the specific payout record. |
rejected | The linked conversion was rejected (e.g., due to a refund). The admin can cancel or leave pending depending on refund policy. |
cancelled | Soft deleted. A second DELETE request permanently removes the record. |
See How Refunds Work for a complete walkthrough of how refunds cascade through conversions and obligations.
Accessing obligation data
# List pending obligations for a collaborator
curl -X GET "https://your-site.com/wp-json/siren/v1/obligations?collaboratorId=42&status=pending&fields=id,value,status,awardType" \
-H "Authorization: Bearer YOUR_TOKEN"
# Get a single obligation with extended fields
curl -X GET "https://your-site.com/wp-json/siren/v1/obligations/15?fields=id,value,status,collaboratorName,programName" \
-H "Authorization: Bearer YOUR_TOKEN" use Siren\Obligations\Core\Datastores\Obligation\Interfaces\ObligationDatastore;
class PayoutReport
{
protected ObligationDatastore $obligations;
public function __construct(ObligationDatastore $obligations)
{
$this->obligations = $obligations;
}
public function getPendingObligations(int $collaboratorId): array
{
return $this->obligations->andWhere([
['column' => 'collaboratorId', 'operator' => '=', 'value' => $collaboratorId],
['column' => 'status', 'operator' => '=', 'value' => 'pending']
]);
}
} use Siren\Obligations\Core\Facades\Obligations;
$pending = Obligations::andWhere([
['column' => 'collaboratorId', 'operator' => '=', 'value' => $collaboratorId],
['column' => 'status', 'operator' => '=', 'value' => 'pending']
]);
$obligation = Obligations::getById(42); Siren is built on an event-driven architecture. Creating obligations manually via PHP bypasses the incentive calculation. The program’s reward rules won’t be applied, the conversion won’t be linked, and the
ObligationIssuedevent won’t fire. Downstream listeners that bind obligations to conversions and trigger fulfillment workflows depend on that event. If you need to issue an obligation, let the pipeline handle it by approving the conversion.
PHP domain methods
Looking up an obligation by conversion
getByConversionId retrieves the obligation that was created for a specific conversion. This is useful when you have a conversion record and need to check whether an obligation was issued and what its current state is. This method is only available through dependency injection.
use Siren\Obligations\Core\Datastores\Obligation\Interfaces\ObligationDatastore;
use PHPNomad\Datastore\Exceptions\RecordNotFoundException;
class ConversionInspector
{
protected ObligationDatastore $obligations;
public function __construct(ObligationDatastore $obligations)
{
$this->obligations = $obligations;
}
public function getObligationForConversion(int $conversionId): ?Obligation
{
try {
return $this->obligations->getByConversionId($conversionId);
} catch (RecordNotFoundException $e) {
return null;
}
}
}
Not every conversion results in an obligation. The incentive system may determine the conversion doesn’t qualify, or the obligation may not have been created yet if the conversion is still pending.
If you already have the conversion’s obligationId, you can skip this lookup and use getById directly:
$obligationId = $conversion->getObligationId();
if ($obligationId !== null) {
$obligation = $this->obligations->getById($obligationId);
}
Querying by payout
To find all obligations that were settled in a specific payout batch:
$settled = $this->obligations->andWhere([
['column' => 'payoutId', 'operator' => '=', 'value' => $payoutId]
]);
Summing owed amounts
There is no built-in sum method, but you can combine a filtered query with array reduction:
$pending = $this->obligations->andWhere([
['column' => 'collaboratorId', 'operator' => '=', 'value' => $collaboratorId],
['column' => 'status', 'operator' => '=', 'value' => 'pending']
]);
$totalOwed = array_reduce($pending, fn(int $sum, Obligation $o) => $sum + $o->getValue(), 0);
Access control (REST)
The list endpoint uses AutoFilterByOwnerMiddleware to enforce access control. Admin users see all obligations across all collaborators, while collaborator-role users are automatically filtered to see only obligations linked to their own collaboratorId. The middleware maps the authenticated user to their collaborator record and injects the filter before the query executes, which is how the collaborator portal shows “my earnings” without exposing other collaborators’ data.
Relationships
Every obligation belongs to exactly one collaborator via collaboratorId.
Upstream, obligations are created as a downstream effect of an approved conversion. The link between the two is maintained via the conversion’s obligationId field.
When an obligation is fulfilled, it is linked to a payout record via payoutId. The payout tracks the actual disbursement to the collaborator. Payouts are grouped into fulfillment batches, so the obligation connects to the fulfillment indirectly through its payout.
REST endpoints
See the individual endpoint pages in the sidebar for full request and response details, or browse the all REST endpoints reference.