Complete Workflows
End-to-end recipes — annuity securitisation, simple invoices, loans, conditional escrow.
End-to-end recipes that compose multiple primitives into a real flow. Each walks through a substantive deal — what the issuer does, what the counterparty does, what the platform settles atomically.
In production these flows are driven by DealPlan — the DMS compiles the plan down to the same primitive calls shown here, with the right signatures collected along the way. The examples below show the underlying mutations directly so you can build the same flow without the deal envelope when that's all you need.
GraphQL gateway for primitive writes. The mutations below go to
$YF_GATEWAY/graphql(api.yieldfabric.com/graphql). Between write steps, poll the message-status endpoint (REST, on payments) at$YF_PAYMENTS/api/users/{user_id}/messages/{message_id}untilexecutedis non-null before proceeding to the next step — every mutation here is async.
Annuity securitisation
An issuer creates future payment obligations totaling 105 AUD over time and atomically swaps them to an investor for an immediate 100 AUD payment. The investor earns 5 AUD over time (5% yield); the issuer gets cash upfront.
The pattern uses self-referential obligations during construction — the issuer is both obligor and counterparty on the obligations until the final swap moves them to the real counterparty. This means the structure can be built up safely without counterparty risk at any intermediate step.
Before you start — polling helper
Every mutation below returns a messageId. Wait for that message to
settle before submitting the next mutation.
export ISSUER_USER_ID=$(curl -s $YF_AUTH/protected/jwt \
-H "Authorization: Bearer $TOKEN" | jq -r .user_id)
export COUNTERPART_USER_ID=$(curl -s $YF_AUTH/protected/jwt \
-H "Authorization: Bearer $COUNTERPART_TOKEN" | jq -r .user_id)
wait_message() {
local token="$1"
local user_id="$2"
local message_id="$3"
until curl -s -H "Authorization: Bearer $token" \
"$YF_PAYMENTS/api/users/$user_id/messages/$message_id" \
| jq -e '.executed' > /dev/null; do
sleep 2
done
}
Step 1 — Issuer creates the 5-day annuity stream
ANNUITY_RESP=$(curl -s -X POST $YF_GATEWAY/graphql \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"query": "mutation($initialPayments: InitialPaymentsInput, $data: JSON) { createObligation(input: { counterpart: \"issuer@yieldfabric.com\", denomination: \"aud-token-asset\", obligor: \"issuer@yieldfabric.com\", notional: \"5\", expiry: \"2027-11-01\", data: $data, initialPayments: $initialPayments }) { success contractId messageId } }",
"variables": {
"data": { "name": "Annuity Stream", "description": "5-day annuity" },
"initialPayments": {
"amount": "5",
"payments": [
{ "unlockSender": "2027-11-01T00:00:00+00:00", "unlockReceiver": "2027-11-01T00:00:00+00:00" },
{ "unlockSender": "2027-11-02T00:00:00+00:00", "unlockReceiver": "2027-11-02T00:00:00+00:00" },
{ "unlockSender": "2027-11-03T00:00:00+00:00", "unlockReceiver": "2027-11-03T00:00:00+00:00" },
{ "unlockSender": "2027-11-04T00:00:00+00:00", "unlockReceiver": "2027-11-04T00:00:00+00:00" },
{ "unlockSender": "2027-11-05T00:00:00+00:00", "unlockReceiver": "2027-11-05T00:00:00+00:00" }
]
}
}
}')
export ANNUITY_CONTRACT_ID=$(echo "$ANNUITY_RESP" | jq -r .data.createObligation.contractId)
export ANNUITY_MSG_ID=$(echo "$ANNUITY_RESP" | jq -r .data.createObligation.messageId)
wait_message "$TOKEN" "$ISSUER_USER_ID" "$ANNUITY_MSG_ID"
Step 2 — Issuer accepts the annuity
Self-referential obligations require the issuer (who's also the counterparty) to accept their own obligation, locking the structure.
ANNUITY_ACCEPT_RESP=$(curl -s -X POST $YF_GATEWAY/graphql \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"query": "mutation { acceptObligation(input: { contractId: \"'$ANNUITY_CONTRACT_ID'\" }) { success messageId } }"
}')
export ANNUITY_ACCEPT_MSG_ID=$(echo "$ANNUITY_ACCEPT_RESP" | jq -r .data.acceptObligation.messageId)
wait_message "$TOKEN" "$ISSUER_USER_ID" "$ANNUITY_ACCEPT_MSG_ID"
Step 3 — Issuer creates the redemption obligation
A single 100 AUD payment due at the end of the schedule.
REDEMPTION_RESP=$(curl -s -X POST $YF_GATEWAY/graphql \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"query": "mutation($initialPayments: InitialPaymentsInput, $data: JSON) { createObligation(input: { counterpart: \"issuer@yieldfabric.com\", denomination: \"aud-token-asset\", obligor: \"issuer@yieldfabric.com\", notional: \"100\", expiry: \"2027-11-01\", data: $data, initialPayments: $initialPayments }) { success contractId messageId } }",
"variables": {
"data": { "name": "Redemption", "description": "Single payment" },
"initialPayments": {
"amount": "100",
"payments": [
{ "unlockSender": "2027-11-06T00:00:00+00:00", "unlockReceiver": "2027-11-06T00:00:00+00:00" }
]
}
}
}')
export REDEMPTION_CONTRACT_ID=$(echo "$REDEMPTION_RESP" | jq -r .data.createObligation.contractId)
export REDEMPTION_MSG_ID=$(echo "$REDEMPTION_RESP" | jq -r .data.createObligation.messageId)
wait_message "$TOKEN" "$ISSUER_USER_ID" "$REDEMPTION_MSG_ID"
Step 4 — Issuer accepts the redemption
REDEMPTION_ACCEPT_RESP=$(curl -s -X POST $YF_GATEWAY/graphql \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"query": "mutation { acceptObligation(input: { contractId: \"'$REDEMPTION_CONTRACT_ID'\" }) { success messageId } }"
}')
export REDEMPTION_ACCEPT_MSG_ID=$(echo "$REDEMPTION_ACCEPT_RESP" | jq -r .data.acceptObligation.messageId)
wait_message "$TOKEN" "$ISSUER_USER_ID" "$REDEMPTION_ACCEPT_MSG_ID"
Step 5 — Issuer creates the atomic swap
Both obligations bundled together, exchanged for the counterparty's 100 AUD upfront payment.
SWAP_RESP=$(curl -s -X POST $YF_GATEWAY/graphql \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"query": "mutation($counterpartyExpectedPayments: InitialPaymentsInput) { createSwap(input: { swapId: \"SWAP-2027-Q4-001\", counterparty: \"counterpart@yieldfabric.com\", deadline: \"2027-11-10\", initiatorObligationIds: [\"'$ANNUITY_CONTRACT_ID'\", \"'$REDEMPTION_CONTRACT_ID'\"], counterpartyExpectedPayments: $counterpartyExpectedPayments }) { success swapId messageId } }",
"variables": {
"counterpartyExpectedPayments": {
"denomination": "aud-token-asset",
"amount": "100",
"payments": [
{ "unlockSender": null, "unlockReceiver": null }
]
}
}
}')
export SWAP_ID=$(echo "$SWAP_RESP" | jq -r .data.createSwap.swapId)
export SWAP_MSG_ID=$(echo "$SWAP_RESP" | jq -r .data.createSwap.messageId)
wait_message "$TOKEN" "$ISSUER_USER_ID" "$SWAP_MSG_ID"
Step 6 — Counterparty completes the swap
The counterparty pays 100 AUD and receives the obligation rights in a single atomic on-chain transition.
COMPLETE_SWAP_RESP=$(curl -s -X POST $YF_GATEWAY/graphql \
-H "Authorization: Bearer $COUNTERPART_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"query": "mutation { completeSwap(input: { swapId: \"'$SWAP_ID'\" }) { success message swapId messageId } }"
}')
export COMPLETE_SWAP_MSG_ID=$(echo "$COMPLETE_SWAP_RESP" | jq -r .data.completeSwap.messageId)
wait_message "$COUNTERPART_TOKEN" "$COUNTERPART_USER_ID" "$COMPLETE_SWAP_MSG_ID"
Result. The issuer received 100 AUD immediately; the counterparty owns rights to 105 AUD across the 5 scheduled payments. Both transfers happened simultaneously — neither side has a window where they've moved value without receiving the other side. All recorded on-chain with full audit trail.
Other common workflows
Simple invoice payment
- Seller creates an obligation with the buyer as counterparty (single payment due on the invoice date).
- Buyer accepts the obligation.
- Payment unlocks on the due date.
- Buyer executes the payment.
- Seller receives funds.
Loan with repayment schedule
- Borrower creates a self-referential repayment obligation with the loan's amortisation schedule.
- Borrower accepts the obligation.
- Lender provides the loan principal via an instant payment.
- The borrower's scheduled repayments unlock automatically over time.
- Lender receives each repayment as it unlocks.
Conditional escrow
- Buyer creates a fully-funded obligation with an oracle condition (e.g. "goods delivered").
- Funds locked, waiting on the oracle.
- Seller ships the goods.
- Oracle posts "delivered" — the unlock condition fires.
- Payment releases to seller.
See also
- Deal Management System (DMS) — for multi-party deals with structured agreements, the DMS owns the lifecycle around the primitives shown here.
- Atomic Swaps — the swap primitive itself.
- Contracts & Obligations — the obligation primitive, including self-referential patterns.