Payers and Payment Counts
Specific, public, one-of, all-of, and K-of-N invoice payment rules, including distinct-payer trackers, shared caps, and independent obligations.
Choose whether success means a total number of payments, a number of distinct payer addresses, or completion by every named person. These require different tracker arrangements even when the total requested amount is identical.
Specific Payer
Use one obligation with one named address and one required payment. Only that address can initiate the payment. The payment completes that obligation, not any other obligations the same address may have in the collection.
Anyone
Use one obligation with payer: { "kind": "anyone" }. With the default count of one, the first eligible successful payment completes it. Payout recipients are excluded from the public payer set. Public eligibility must be determined from the actual standard; an empty named roster is not equivalent to anyone.
Two people may see an open slot simultaneously. The chain serializes successful transactions and enforces the shared cap; the later transaction can fail after the first consumes it. The UI's open state is not a reservation.
One of a Roster
Use one obligation with a named roster and one required payment. Any eligible member can satisfy it; the other members owe nothing under that obligation after it completes.
For example, “Alice or Bob pays 10 units” is one shared obligation. Creating a separate obligation for each would instead require both to pay.
Every Named Payer
Create a separate obligation for each unique payer. Each has its own payout amount and tracker. This allows unequal shares: Alice can owe 7 units and Bob 3 units. The invoice is complete only after both obligations are complete.
One person's extra payment cannot fill another person's share. Do not infer all-of completion from a total collected amount. A roster-level count without a per-payer constraint cannot prove every member paid.
The all-of form preset uses a unique address for each share. This is a preset rule, not a global ban on repeated addresses: installments intentionally use the same payer in different obligations.
K Distinct Payers
Distinctness is enforced per initiating wallet address. One person can control several addresses; this standard does not verify unique human identities.
Use one obligation with requiredPayments: "K" and distinctPayers: true. The payer can be a named roster or anyone. Every successful payer pays the same fixed payout shape once. A named roster must contain enough eligible distinct addresses for K payments.
The overall tracker caps total payment count. A per-initiator tracker caps each payer at one. Both are necessary. A single payer submitting several transactions must not be counted as several payer addresses. Recipient exclusions still apply for a public roster.
K-of-N is satisfied by any K eligible payers; it does not select them in advance or require the remaining N−K members to participate.
Repeated Fixed Payments
Valid custom terms can require several payments without distinctPayers. In that case the same eligible payer may consume several slots in separate successful payments. The UI may classify this as custom because it does not mean K distinct payer addresses.
Do not silently enable distinct-payer tracking when displaying or parsing these terms. Preserve the original semantics across the create form, developer table, payment action, SDK parser, and indexer.
Tracking Identity
Track every obligation using its configured collection, approval, approval level, tracker ID/type, and applicable address. Do not derive a tracker key solely from a display reference or query arbitrary per-address trackers when the approval does not configure them.
Eligibility, progress, and lifecycle are separate. An eligible payer can have already consumed their per-payer slot while the overall obligation remains open for others. Missing tracker evidence is unknown, not zero. See Payment State and Tracking.
JSON Recipes
The following obligations arrays replace the array in the complete invoice JSON, with kind: "invoice". They are fragments, not standalone builder inputs. Replace demo addresses and timestamps. Each recipe can also be generated as a complete input with bb build payment-request-v2 --example <name>.
Specific
{
"obligations": [
{
"id": "payment-1",
"payer": {
"kind": "addresses",
"addresses": [
"bb1zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zql3w7"
]
},
"payouts": [
{
"recipient": "bb1xvenxvenxvenxvenxvenxvenxvenxvenlrd2nm",
"denom": "ubadge",
"amount": "1000000"
}
],
"startTime": "1",
"endTime": "1893456000000"
}
]
}Generate the complete specific input, edit its demo values, and build it:
bb build payment-request-v2 --example specific | jq '.data' > specific.json
# Edit specific.json before building.
bb build payment-request-v2 --json specific.json --creator "$CREATOR" --output-file specific-proposal.jsonOne
{
"obligations": [
{
"id": "payment-1",
"payer": {
"kind": "addresses",
"addresses": [
"bb1zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zql3w7",
"bb1yg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zda6hxf"
]
},
"payouts": [
{
"recipient": "bb1xvenxvenxvenxvenxvenxvenxvenxvenlrd2nm",
"denom": "ubadge",
"amount": "1000000"
}
],
"startTime": "1",
"endTime": "1893456000000"
}
]
}Generate the complete one input, edit its demo values, and build it:
bb build payment-request-v2 --example one | jq '.data' > one.json
# Edit one.json before building.
bb build payment-request-v2 --json one.json --creator "$CREATOR" --output-file one-proposal.jsonAll
{
"obligations": [
{
"id": "payment-1",
"payer": {
"kind": "addresses",
"addresses": [
"bb1zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zql3w7"
]
},
"payouts": [
{
"recipient": "bb1xvenxvenxvenxvenxvenxvenxvenxvenlrd2nm",
"denom": "ubadge",
"amount": "1000000"
}
],
"startTime": "1",
"endTime": "1893456000000"
},
{
"id": "payment-2",
"payer": {
"kind": "addresses",
"addresses": [
"bb1yg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zda6hxf"
]
},
"payouts": [
{
"recipient": "bb1xvenxvenxvenxvenxvenxvenxvenxvenlrd2nm",
"denom": "ubadge",
"amount": "2000000"
}
],
"startTime": "1",
"endTime": "1896048000000"
}
]
}Generate the complete all input, edit its demo values, and build it:
bb build payment-request-v2 --example all | jq '.data' > all.json
# Edit all.json before building.
bb build payment-request-v2 --json all.json --creator "$CREATOR" --output-file all-proposal.jsonThreshold
{
"obligations": [
{
"id": "payment-1",
"payer": {
"kind": "addresses",
"addresses": [
"bb1zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zql3w7",
"bb1yg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zda6hxf",
"bb1g3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyqv9444"
]
},
"payouts": [
{
"recipient": "bb1xvenxvenxvenxvenxvenxvenxvenxvenlrd2nm",
"denom": "ubadge",
"amount": "1000000"
}
],
"startTime": "1",
"endTime": "1893456000000",
"requiredPayments": "2",
"distinctPayers": true
}
]
}Generate the complete threshold input, edit its demo values, and build it:
bb build payment-request-v2 --example threshold | jq '.data' > threshold.json
# Edit threshold.json before building.
bb build payment-request-v2 --json threshold.json --creator "$CREATOR" --output-file threshold-proposal.json