<!-- x-generated: Generated by the Hyperscale artifact pipeline; do not edit by hand -->

# BNPL Checkout

> The frozen BNPL Checkout Product Build, version 1.

- Composition id: `bnpl_checkout`
- Version: 1
- Digest: `4f5ef36e4bc4789e3bec5b55b364ddb09d510038d2f50e468b7b245d09a6e7f7`
- Currencies: `SAR`
- Webhook events: 29

## Instruments

| Instrument | Title | States | Actions |
| --- | --- | --- | --- |
| [`collections_case`](../instruments/collections_case.md) | Collections case | 6 | create |
| [`contact_attempt`](../instruments/contact_attempt.md) | Contact attempt | 2 | create |
| [`credit_agreement`](../instruments/credit_agreement.md) | Credit agreement | 6 | advance, create, settle |
| [`exposure_arrangement`](../instruments/exposure_arrangement.md) | Exposure arrangement | 4 | create |
| [`funding_commitment`](../instruments/funding_commitment.md) | Funding commitment | 3 | create |
| [`funding_round`](../instruments/funding_round.md) | Funding round | 4 | create |
| [`installment`](../instruments/installment.md) | Installment | 4 | create |

## Journeys

### Advance and write off credit

- Journey id: `advanced_credit_written_off`
- Steps: 10

1. `sandbox_customer_onboard`
   - Actor: product
   - Action: platform write
   - Route: `POST /v1/sandbox/customer-onboarding`, scopes customer:write, idempotency required, receipt yes
   - Body: displayName, productId
   - Path: none
   - Query: none
   - Expect: none
   - Bindings: none
2. `sandbox_customer_access_activate`
   - Actor: product
   - Action: platform write
   - Route: `POST /v1/sandbox/customer-access/{customerAccessId}/activate`, scopes customer:write, idempotency required, receipt yes
   - Body: none
   - Path: customerAccessId
   - Query: none
   - Expect: none
   - Bindings: customerAccessId <- borrower_customer.customerAccessId
3. `account_create`
   - Actor: product
   - Action: platform write
   - Route: `POST /v1/accounts`, scopes account:write, idempotency required, receipt yes
   - Body: currency, owner, role
   - Path: none
   - Query: none
   - Expect: none
   - Bindings: owner.id <- borrower_customer.customerId
4. `sandbox_customer_onboard`
   - Actor: product
   - Action: platform write
   - Route: `POST /v1/sandbox/customer-onboarding`, scopes customer:write, idempotency required, receipt yes
   - Body: displayName, productId
   - Path: none
   - Query: none
   - Expect: none
   - Bindings: none
5. `sandbox_customer_access_activate`
   - Actor: product
   - Action: platform write
   - Route: `POST /v1/sandbox/customer-access/{customerAccessId}/activate`, scopes customer:write, idempotency required, receipt yes
   - Body: none
   - Path: customerAccessId
   - Query: none
   - Expect: none
   - Bindings: customerAccessId <- lender_customer.customerAccessId
6. `account_create`
   - Actor: product
   - Action: platform write
   - Route: `POST /v1/accounts`, scopes account:write, idempotency required, receipt yes
   - Body: currency, owner, role
   - Path: none
   - Query: none
   - Expect: none
   - Bindings: owner.id <- lender_customer.customerId
7. `sandbox_account_fund`
   - Actor: product
   - Action: platform write
   - Route: `POST /v1/sandbox/account-funding`, scopes transfer:write, idempotency required, receipt yes
   - Body: amount, currency, destinationAccountId
   - Path: none
   - Query: none
   - Expect: none
   - Bindings: destinationAccountId <- lender.accountId
8. `credit_agreement_create`
   - Actor: product
   - Action: `credit_agreement.create`
   - Route: `POST /v1/credit-agreements`, scopes credit_agreement:write, idempotency required, receipt yes
   - Body: borrowerAccountId, currency, fundingAccountId, principal, productId
   - Path: none
   - Query: none
   - Expect: none
   - Bindings: borrowerAccountId <- borrower.accountId, fundingAccountId <- lender.accountId
9. `credit_agreement_advance`
   - Actor: product
   - Action: `credit_agreement.advance`
   - Route: `POST /v1/credit-agreements/{creditAgreementId}/advance`, scopes credit_agreement:write, transfer:write, idempotency required, receipt yes
   - Body: none
   - Path: creditAgreementId
   - Query: none
   - Expect: none
   - Bindings: creditAgreementId <- agreement.creditAgreementId
10. `credit_agreement_write_off`
   - Actor: product
   - Action: platform write
   - Route: `POST /v1/credit-agreements/{creditAgreementId}/write-off`, scopes credit_agreement:write, idempotency required, receipt yes
   - Body: none
   - Path: creditAgreementId
   - Query: none
   - Expect: none
   - Bindings: creditAgreementId <- agreement.creditAgreementId

### Write off an overdue installment

- Journey id: `write_off_overdue_installment`
- Steps: 10

1. `sandbox_customer_onboard`
   - Actor: product
   - Action: platform write
   - Route: `POST /v1/sandbox/customer-onboarding`, scopes customer:write, idempotency required, receipt yes
   - Body: displayName, productId
   - Path: none
   - Query: none
   - Expect: none
   - Bindings: none
2. `sandbox_customer_access_activate`
   - Actor: product
   - Action: platform write
   - Route: `POST /v1/sandbox/customer-access/{customerAccessId}/activate`, scopes customer:write, idempotency required, receipt yes
   - Body: none
   - Path: customerAccessId
   - Query: none
   - Expect: none
   - Bindings: customerAccessId <- customer.customerAccessId
3. `account_create`
   - Actor: product
   - Action: platform write
   - Route: `POST /v1/accounts`, scopes account:write, idempotency required, receipt yes
   - Body: currency, owner, role
   - Path: none
   - Query: none
   - Expect: none
   - Bindings: owner.id <- customer.customerId
4. `account_create`
   - Actor: product
   - Action: platform write
   - Route: `POST /v1/accounts`, scopes account:write, idempotency required, receipt yes
   - Body: currency, owner, role
   - Path: none
   - Query: none
   - Expect: none
   - Bindings: owner.id <- customer.customerId
5. `sandbox_account_fund`
   - Actor: product
   - Action: platform write
   - Route: `POST /v1/sandbox/account-funding`, scopes transfer:write, idempotency required, receipt yes
   - Body: amount, currency, destinationAccountId
   - Path: none
   - Query: none
   - Expect: none
   - Bindings: destinationAccountId <- funding_account.accountId
6. `credit_agreement_create`
   - Actor: product
   - Action: `credit_agreement.create`
   - Route: `POST /v1/credit-agreements`, scopes credit_agreement:write, idempotency required, receipt yes
   - Body: borrowerAccountId, currency, fundingAccountId, principal, productId
   - Path: none
   - Query: none
   - Expect: none
   - Bindings: borrowerAccountId <- borrower_account.accountId, fundingAccountId <- funding_account.accountId
7. `credit_agreement_advance`
   - Actor: product
   - Action: `credit_agreement.advance`
   - Route: `POST /v1/credit-agreements/{creditAgreementId}/advance`, scopes credit_agreement:write, transfer:write, idempotency required, receipt yes
   - Body: none
   - Path: creditAgreementId
   - Query: none
   - Expect: none
   - Bindings: creditAgreementId <- agreement.creditAgreementId
8. `installment_create`
   - Actor: product
   - Action: `installment.create`
   - Route: `POST /v1/installments`, scopes installment:write, idempotency required, receipt yes
   - Body: agreementId, amount, borrowerAccountId, currency, dueAt, fundingAccountId, productId
   - Path: none
   - Query: none
   - Expect: none
   - Bindings: agreementId <- agreement.creditAgreementId, borrowerAccountId <- borrower_account.accountId, fundingAccountId <- funding_account.accountId
9. `sandbox_platform_fire`
   - Actor: platform
   - Sandbox trigger: The platform fires `installment.mark_overdue` in normal lifecycle processing; sandbox fires it now.
   - Action: platform lifecycle trigger
   - Route: `POST /v1/sandbox/platform/fire`, scopes no scopes required, idempotency required, receipt yes
   - Body: input, operation
   - Path: none
   - Query: none
   - Expect: none
   - Bindings: input.installmentId <- installment.installmentId
10. `installment_write_off`
   - Actor: product
   - Action: platform write
   - Route: `POST /v1/installments/{installmentId}/write-off`, scopes installment:write, idempotency required, receipt yes
   - Body: none
   - Path: installmentId
   - Query: none
   - Expect: none
   - Bindings: installmentId <- installment.installmentId

### Approve and close an exposure

- Journey id: `approved_exposure_closed`
- Steps: 6

1. `sandbox_customer_onboard`
   - Actor: product
   - Action: platform write
   - Route: `POST /v1/sandbox/customer-onboarding`, scopes customer:write, idempotency required, receipt yes
   - Body: displayName, productId
   - Path: none
   - Query: none
   - Expect: none
   - Bindings: none
2. `sandbox_customer_access_activate`
   - Actor: product
   - Action: platform write
   - Route: `POST /v1/sandbox/customer-access/{customerAccessId}/activate`, scopes customer:write, idempotency required, receipt yes
   - Body: none
   - Path: customerAccessId
   - Query: none
   - Expect: none
   - Bindings: customerAccessId <- customer.customerAccessId
3. `account_create`
   - Actor: product
   - Action: platform write
   - Route: `POST /v1/accounts`, scopes account:write, idempotency required, receipt yes
   - Body: currency, owner, role
   - Path: none
   - Query: none
   - Expect: none
   - Bindings: owner.id <- customer.customerId
4. `exposure_arrangement_create`
   - Actor: product
   - Action: `exposure_arrangement.create`
   - Route: `POST /v1/exposure-arrangements`, scopes exposure_arrangement:write, idempotency required, receipt yes
   - Body: approvedAmountsCapAmount, borrowerAccountId, borrowerEntityId, currency, productId
   - Path: none
   - Query: none
   - Expect: none
   - Bindings: borrowerAccountId <- borrower_account.accountId
5. `exposure_arrangement_approve`
   - Actor: product
   - Action: platform write
   - Route: `POST /v1/exposure-arrangements/{exposureArrangementId}/approve`, scopes exposure_arrangement:write, idempotency required, receipt yes
   - Body: none
   - Path: exposureArrangementId
   - Query: none
   - Expect: none
   - Bindings: exposureArrangementId <- arrangement.exposureArrangementId
6. `exposure_arrangement_close`
   - Actor: product
   - Action: platform write
   - Route: `POST /v1/exposure-arrangements/{exposureArrangementId}/close`, scopes exposure_arrangement:write, idempotency required, receipt yes
   - Body: none
   - Path: exposureArrangementId
   - Query: none
   - Expect: none
   - Bindings: exposureArrangementId <- arrangement.exposureArrangementId

### Assign and recall a collections case

- Journey id: `collections_case_recalled`
- Steps: 14

1. `sandbox_customer_onboard`
   - Actor: product
   - Action: platform write
   - Route: `POST /v1/sandbox/customer-onboarding`, scopes customer:write, idempotency required, receipt yes
   - Body: displayName, productId
   - Path: none
   - Query: none
   - Expect: none
   - Bindings: none
2. `sandbox_customer_access_activate`
   - Actor: product
   - Action: platform write
   - Route: `POST /v1/sandbox/customer-access/{customerAccessId}/activate`, scopes customer:write, idempotency required, receipt yes
   - Body: none
   - Path: customerAccessId
   - Query: none
   - Expect: none
   - Bindings: customerAccessId <- borrower_customer.customerAccessId
3. `account_create`
   - Actor: product
   - Action: platform write
   - Route: `POST /v1/accounts`, scopes account:write, idempotency required, receipt yes
   - Body: currency, owner, role
   - Path: none
   - Query: none
   - Expect: none
   - Bindings: owner.id <- borrower_customer.customerId
4. `sandbox_customer_onboard`
   - Actor: product
   - Action: platform write
   - Route: `POST /v1/sandbox/customer-onboarding`, scopes customer:write, idempotency required, receipt yes
   - Body: displayName, productId
   - Path: none
   - Query: none
   - Expect: none
   - Bindings: none
5. `sandbox_customer_access_activate`
   - Actor: product
   - Action: platform write
   - Route: `POST /v1/sandbox/customer-access/{customerAccessId}/activate`, scopes customer:write, idempotency required, receipt yes
   - Body: none
   - Path: customerAccessId
   - Query: none
   - Expect: none
   - Bindings: customerAccessId <- lender_customer.customerAccessId
6. `account_create`
   - Actor: product
   - Action: platform write
   - Route: `POST /v1/accounts`, scopes account:write, idempotency required, receipt yes
   - Body: currency, owner, role
   - Path: none
   - Query: none
   - Expect: none
   - Bindings: owner.id <- lender_customer.customerId
7. `sandbox_account_fund`
   - Actor: product
   - Action: platform write
   - Route: `POST /v1/sandbox/account-funding`, scopes transfer:write, idempotency required, receipt yes
   - Body: amount, currency, destinationAccountId
   - Path: none
   - Query: none
   - Expect: none
   - Bindings: destinationAccountId <- lender.accountId
8. `credit_agreement_create`
   - Actor: product
   - Action: `credit_agreement.create`
   - Route: `POST /v1/credit-agreements`, scopes credit_agreement:write, idempotency required, receipt yes
   - Body: borrowerAccountId, currency, fundingAccountId, principal, productId
   - Path: none
   - Query: none
   - Expect: none
   - Bindings: borrowerAccountId <- borrower.accountId, fundingAccountId <- lender.accountId
9. `credit_agreement_advance`
   - Actor: product
   - Action: `credit_agreement.advance`
   - Route: `POST /v1/credit-agreements/{creditAgreementId}/advance`, scopes credit_agreement:write, transfer:write, idempotency required, receipt yes
   - Body: none
   - Path: creditAgreementId
   - Query: none
   - Expect: none
   - Bindings: creditAgreementId <- agreement.creditAgreementId
10. `installment_create`
   - Actor: product
   - Action: `installment.create`
   - Route: `POST /v1/installments`, scopes installment:write, idempotency required, receipt yes
   - Body: agreementId, amount, borrowerAccountId, currency, dueAt, fundingAccountId, productId
   - Path: none
   - Query: none
   - Expect: none
   - Bindings: agreementId <- agreement.creditAgreementId, borrowerAccountId <- borrower.accountId, fundingAccountId <- lender.accountId
11. `sandbox_platform_fire`
   - Actor: platform
   - Sandbox trigger: The platform fires `installment.mark_overdue` in normal lifecycle processing; sandbox fires it now.
   - Action: platform lifecycle trigger
   - Route: `POST /v1/sandbox/platform/fire`, scopes no scopes required, idempotency required, receipt yes
   - Body: input, operation
   - Path: none
   - Query: none
   - Expect: none
   - Bindings: input.installmentId <- installment.installmentId
12. `collections_case_create`
   - Actor: product
   - Action: `collections_case.create`
   - Route: `POST /v1/collections-cases`, scopes collections_case:write, idempotency required, receipt yes
   - Body: agreementId, allowedContactEndHour, allowedContactStartHour, currency, debtorAccountId, maxContactAttemptsPer30Days, memo, productId, recoveryAccountId, referredAmount, triggeringInstallmentId
   - Path: none
   - Query: none
   - Expect: none
   - Bindings: agreementId <- agreement.creditAgreementId, debtorAccountId <- borrower.accountId, recoveryAccountId <- lender.accountId, triggeringInstallmentId <- installment.installmentId
13. `collections_case_assign`
   - Actor: product
   - Action: platform write
   - Route: `POST /v1/collections-cases/{collectionsCaseId}/assign`, scopes collections_case:write, idempotency required, receipt yes
   - Body: none
   - Path: collectionsCaseId
   - Query: none
   - Expect: none
   - Bindings: collectionsCaseId <- case.collectionsCaseId
14. `collections_case_recall`
   - Actor: product
   - Action: platform write
   - Route: `POST /v1/collections-cases/{collectionsCaseId}/recall`, scopes collections_case:write, idempotency required, receipt yes
   - Body: none
   - Path: collectionsCaseId
   - Query: none
   - Expect: none
   - Bindings: collectionsCaseId <- case.collectionsCaseId

### Record and void a contact attempt

- Journey id: `contact_attempt_voided`
- Steps: 15

1. `sandbox_customer_onboard`
   - Actor: product
   - Action: platform write
   - Route: `POST /v1/sandbox/customer-onboarding`, scopes customer:write, idempotency required, receipt yes
   - Body: displayName, productId
   - Path: none
   - Query: none
   - Expect: none
   - Bindings: none
2. `sandbox_customer_access_activate`
   - Actor: product
   - Action: platform write
   - Route: `POST /v1/sandbox/customer-access/{customerAccessId}/activate`, scopes customer:write, idempotency required, receipt yes
   - Body: none
   - Path: customerAccessId
   - Query: none
   - Expect: none
   - Bindings: customerAccessId <- borrower_customer.customerAccessId
3. `account_create`
   - Actor: product
   - Action: platform write
   - Route: `POST /v1/accounts`, scopes account:write, idempotency required, receipt yes
   - Body: currency, owner, role
   - Path: none
   - Query: none
   - Expect: none
   - Bindings: owner.id <- borrower_customer.customerId
4. `sandbox_customer_onboard`
   - Actor: product
   - Action: platform write
   - Route: `POST /v1/sandbox/customer-onboarding`, scopes customer:write, idempotency required, receipt yes
   - Body: displayName, productId
   - Path: none
   - Query: none
   - Expect: none
   - Bindings: none
5. `sandbox_customer_access_activate`
   - Actor: product
   - Action: platform write
   - Route: `POST /v1/sandbox/customer-access/{customerAccessId}/activate`, scopes customer:write, idempotency required, receipt yes
   - Body: none
   - Path: customerAccessId
   - Query: none
   - Expect: none
   - Bindings: customerAccessId <- lender_customer.customerAccessId
6. `account_create`
   - Actor: product
   - Action: platform write
   - Route: `POST /v1/accounts`, scopes account:write, idempotency required, receipt yes
   - Body: currency, owner, role
   - Path: none
   - Query: none
   - Expect: none
   - Bindings: owner.id <- lender_customer.customerId
7. `sandbox_account_fund`
   - Actor: product
   - Action: platform write
   - Route: `POST /v1/sandbox/account-funding`, scopes transfer:write, idempotency required, receipt yes
   - Body: amount, currency, destinationAccountId
   - Path: none
   - Query: none
   - Expect: none
   - Bindings: destinationAccountId <- lender.accountId
8. `credit_agreement_create`
   - Actor: product
   - Action: `credit_agreement.create`
   - Route: `POST /v1/credit-agreements`, scopes credit_agreement:write, idempotency required, receipt yes
   - Body: borrowerAccountId, currency, fundingAccountId, principal, productId
   - Path: none
   - Query: none
   - Expect: none
   - Bindings: borrowerAccountId <- borrower.accountId, fundingAccountId <- lender.accountId
9. `credit_agreement_advance`
   - Actor: product
   - Action: `credit_agreement.advance`
   - Route: `POST /v1/credit-agreements/{creditAgreementId}/advance`, scopes credit_agreement:write, transfer:write, idempotency required, receipt yes
   - Body: none
   - Path: creditAgreementId
   - Query: none
   - Expect: none
   - Bindings: creditAgreementId <- agreement.creditAgreementId
10. `installment_create`
   - Actor: product
   - Action: `installment.create`
   - Route: `POST /v1/installments`, scopes installment:write, idempotency required, receipt yes
   - Body: agreementId, amount, borrowerAccountId, currency, dueAt, fundingAccountId, productId
   - Path: none
   - Query: none
   - Expect: none
   - Bindings: agreementId <- agreement.creditAgreementId, borrowerAccountId <- borrower.accountId, fundingAccountId <- lender.accountId
11. `sandbox_platform_fire`
   - Actor: platform
   - Sandbox trigger: The platform fires `installment.mark_overdue` in normal lifecycle processing; sandbox fires it now.
   - Action: platform lifecycle trigger
   - Route: `POST /v1/sandbox/platform/fire`, scopes no scopes required, idempotency required, receipt yes
   - Body: input, operation
   - Path: none
   - Query: none
   - Expect: none
   - Bindings: input.installmentId <- installment.installmentId
12. `collections_case_create`
   - Actor: product
   - Action: `collections_case.create`
   - Route: `POST /v1/collections-cases`, scopes collections_case:write, idempotency required, receipt yes
   - Body: agreementId, allowedContactEndHour, allowedContactStartHour, currency, debtorAccountId, maxContactAttemptsPer30Days, memo, productId, recoveryAccountId, referredAmount, triggeringInstallmentId
   - Path: none
   - Query: none
   - Expect: none
   - Bindings: agreementId <- agreement.creditAgreementId, debtorAccountId <- borrower.accountId, recoveryAccountId <- lender.accountId, triggeringInstallmentId <- installment.installmentId
13. `collections_case_assign`
   - Actor: product
   - Action: platform write
   - Route: `POST /v1/collections-cases/{collectionsCaseId}/assign`, scopes collections_case:write, idempotency required, receipt yes
   - Body: none
   - Path: collectionsCaseId
   - Query: none
   - Expect: none
   - Bindings: collectionsCaseId <- case.collectionsCaseId
14. `contact_attempt_create`
   - Actor: product
   - Action: `contact_attempt.create`
   - Route: `POST /v1/contact-attempts`, scopes contact_attempt:write, idempotency required, receipt yes
   - Body: attemptedAt, caseId, channel, note, outcome, productId
   - Path: none
   - Query: none
   - Expect: none
   - Bindings: caseId <- case.collectionsCaseId
15. `contact_attempt_void`
   - Actor: product
   - Action: platform write
   - Route: `POST /v1/contact-attempts/{contactAttemptId}/void`, scopes contact_attempt:write, idempotency required, receipt yes
   - Body: none
   - Path: contactAttemptId
   - Query: none
   - Expect: none
   - Bindings: contactAttemptId <- attempt.contactAttemptId

### Fail an underfunded round

- Journey id: `underfunded_round_failed`
- Steps: 5

1. `sandbox_customer_onboard`
   - Actor: product
   - Action: platform write
   - Route: `POST /v1/sandbox/customer-onboarding`, scopes customer:write, idempotency required, receipt yes
   - Body: displayName, productId
   - Path: none
   - Query: none
   - Expect: none
   - Bindings: none
2. `sandbox_customer_access_activate`
   - Actor: product
   - Action: platform write
   - Route: `POST /v1/sandbox/customer-access/{customerAccessId}/activate`, scopes customer:write, idempotency required, receipt yes
   - Body: none
   - Path: customerAccessId
   - Query: none
   - Expect: none
   - Bindings: customerAccessId <- customer.customerAccessId
3. `account_create`
   - Actor: product
   - Action: platform write
   - Route: `POST /v1/accounts`, scopes account:write, idempotency required, receipt yes
   - Body: currency, owner, role
   - Path: none
   - Query: none
   - Expect: none
   - Bindings: owner.id <- customer.customerId
4. `funding_round_create`
   - Actor: product
   - Action: `funding_round.create`
   - Route: `POST /v1/funding-rounds`, scopes funding_round:write, idempotency required, receipt yes
   - Body: borrowerAccountId, closeBy, currency, memo, productId, targetAmount
   - Path: none
   - Query: none
   - Expect: none
   - Bindings: borrowerAccountId <- borrower.accountId
5. `sandbox_platform_fire`
   - Actor: platform
   - Sandbox trigger: The platform fires `funding_round.fail` in normal lifecycle processing; sandbox fires it now.
   - Action: platform lifecycle trigger
   - Route: `POST /v1/sandbox/platform/fire`, scopes no scopes required, idempotency required, receipt yes
   - Body: input, operation
   - Path: none
   - Query: none
   - Expect: none
   - Bindings: input.fundingRoundId <- round.fundingRoundId

### Refund a failed pledge

- Journey id: `failed_pledge_refunded`
- Steps: 11

1. `sandbox_customer_onboard`
   - Actor: product
   - Action: platform write
   - Route: `POST /v1/sandbox/customer-onboarding`, scopes customer:write, idempotency required, receipt yes
   - Body: displayName, productId
   - Path: none
   - Query: none
   - Expect: none
   - Bindings: none
2. `sandbox_customer_access_activate`
   - Actor: product
   - Action: platform write
   - Route: `POST /v1/sandbox/customer-access/{customerAccessId}/activate`, scopes customer:write, idempotency required, receipt yes
   - Body: none
   - Path: customerAccessId
   - Query: none
   - Expect: none
   - Bindings: customerAccessId <- borrower_customer.customerAccessId
3. `account_create`
   - Actor: product
   - Action: platform write
   - Route: `POST /v1/accounts`, scopes account:write, idempotency required, receipt yes
   - Body: currency, owner, role
   - Path: none
   - Query: none
   - Expect: none
   - Bindings: owner.id <- borrower_customer.customerId
4. `sandbox_customer_onboard`
   - Actor: product
   - Action: platform write
   - Route: `POST /v1/sandbox/customer-onboarding`, scopes customer:write, idempotency required, receipt yes
   - Body: displayName, productId
   - Path: none
   - Query: none
   - Expect: none
   - Bindings: none
5. `sandbox_customer_access_activate`
   - Actor: product
   - Action: platform write
   - Route: `POST /v1/sandbox/customer-access/{customerAccessId}/activate`, scopes customer:write, idempotency required, receipt yes
   - Body: none
   - Path: customerAccessId
   - Query: none
   - Expect: none
   - Bindings: customerAccessId <- investor_customer.customerAccessId
6. `account_create`
   - Actor: product
   - Action: platform write
   - Route: `POST /v1/accounts`, scopes account:write, idempotency required, receipt yes
   - Body: currency, owner, role
   - Path: none
   - Query: none
   - Expect: none
   - Bindings: owner.id <- investor_customer.customerId
7. `sandbox_account_fund`
   - Actor: product
   - Action: platform write
   - Route: `POST /v1/sandbox/account-funding`, scopes transfer:write, idempotency required, receipt yes
   - Body: amount, currency, destinationAccountId
   - Path: none
   - Query: none
   - Expect: none
   - Bindings: destinationAccountId <- investor.accountId
8. `funding_round_create`
   - Actor: product
   - Action: `funding_round.create`
   - Route: `POST /v1/funding-rounds`, scopes funding_round:write, idempotency required, receipt yes
   - Body: borrowerAccountId, closeBy, currency, memo, productId, targetAmount
   - Path: none
   - Query: none
   - Expect: none
   - Bindings: borrowerAccountId <- borrower.accountId
9. `funding_commitment_create`
   - Actor: product
   - Action: `funding_commitment.create`
   - Route: `POST /v1/funding-commitments`, scopes funding_commitment:write, transfer:write, idempotency required, receipt yes
   - Body: amount, committedAt, fundingRoundId, investorAccountId, productId
   - Path: none
   - Query: none
   - Expect: none
   - Bindings: fundingRoundId <- round.fundingRoundId, investorAccountId <- investor.accountId
10. `sandbox_platform_fire`
   - Actor: platform
   - Sandbox trigger: The platform fires `funding_round.fail` in normal lifecycle processing; sandbox fires it now.
   - Action: platform lifecycle trigger
   - Route: `POST /v1/sandbox/platform/fire`, scopes no scopes required, idempotency required, receipt yes
   - Body: input, operation
   - Path: none
   - Query: none
   - Expect: none
   - Bindings: input.fundingRoundId <- round.fundingRoundId
11. `sandbox_platform_fire`
   - Actor: platform
   - Sandbox trigger: The platform fires `funding_commitment.refund` in normal lifecycle processing; sandbox fires it now.
   - Action: platform lifecycle trigger
   - Route: `POST /v1/sandbox/platform/fire`, scopes no scopes required, idempotency required, receipt yes
   - Body: input, operation
   - Path: none
   - Query: none
   - Expect: none
   - Bindings: input.fundingCommitmentId <- commitment.fundingCommitmentId

### Advance and settle a plan

- Journey id: `bnpl_plan_settlement`
- Steps: 11

1. `sandbox_customer_onboard`
   - Actor: product
   - Action: platform write
   - Route: `POST /v1/sandbox/customer-onboarding`, scopes customer:write, idempotency required, receipt yes
   - Body: displayName, productId
   - Path: none
   - Query: none
   - Expect: none
   - Bindings: none
2. `sandbox_customer_access_activate`
   - Actor: product
   - Action: platform write
   - Route: `POST /v1/sandbox/customer-access/{customerAccessId}/activate`, scopes customer:write, idempotency required, receipt yes
   - Body: none
   - Path: customerAccessId
   - Query: none
   - Expect: none
   - Bindings: customerAccessId <- shopper.customerAccessId
3. `account_create`
   - Actor: product
   - Action: platform write
   - Route: `POST /v1/accounts`, scopes account:write, idempotency required, receipt yes
   - Body: currency, owner, role
   - Path: none
   - Query: none
   - Expect: none
   - Bindings: owner.id <- shopper.customerId
4. `sandbox_account_fund`
   - Actor: product
   - Action: platform write
   - Route: `POST /v1/sandbox/account-funding`, scopes transfer:write, idempotency required, receipt yes
   - Body: amount, currency, destinationAccountId
   - Path: none
   - Query: none
   - Expect: none
   - Bindings: destinationAccountId <- shopper_account.accountId
5. `account_create`
   - Actor: product
   - Action: platform write
   - Route: `POST /v1/accounts`, scopes account:write, idempotency required, receipt yes
   - Body: currency, owner, role
   - Path: none
   - Query: none
   - Expect: none
   - Bindings: owner.id <- shopper.customerId
6. `sandbox_account_fund`
   - Actor: product
   - Action: platform write
   - Route: `POST /v1/sandbox/account-funding`, scopes transfer:write, idempotency required, receipt yes
   - Body: amount, currency, destinationAccountId
   - Path: none
   - Query: none
   - Expect: none
   - Bindings: destinationAccountId <- pool_account.accountId
7. `credit_agreement_create`
   - Actor: product
   - Action: `credit_agreement.create`
   - Route: `POST /v1/credit-agreements`, scopes credit_agreement:write, idempotency required, receipt yes
   - Body: borrowerAccountId, currency, fundingAccountId, principal, productId
   - Path: none
   - Query: none
   - Expect: none
   - Bindings: borrowerAccountId <- shopper_account.accountId, fundingAccountId <- pool_account.accountId
8. `credit_agreement_advance`
   - Actor: product
   - Action: `credit_agreement.advance`
   - Route: `POST /v1/credit-agreements/{creditAgreementId}/advance`, scopes credit_agreement:write, transfer:write, idempotency required, receipt yes
   - Body: none
   - Path: creditAgreementId
   - Query: none
   - Expect: none
   - Bindings: creditAgreementId <- plan.creditAgreementId
9. `installment_create`
   - Actor: product
   - Action: `installment.create`
   - Route: `POST /v1/installments`, scopes installment:write, idempotency required, receipt yes
   - Body: agreementId, amount, borrowerAccountId, currency, dueAt, fundingAccountId, productId
   - Path: none
   - Query: none
   - Expect: none
   - Bindings: agreementId <- plan.creditAgreementId, borrowerAccountId <- shopper_account.accountId, fundingAccountId <- pool_account.accountId
10. `sandbox_platform_fire`
   - Actor: platform
   - Sandbox trigger: The platform fires `installment.collect` in normal lifecycle processing; sandbox fires it now.
   - Action: platform lifecycle trigger
   - Route: `POST /v1/sandbox/platform/fire`, scopes no scopes required, idempotency required, receipt yes
   - Body: input, operation
   - Path: none
   - Query: none
   - Expect: none
   - Bindings: input.installmentId <- payment.installmentId
11. `credit_agreement_settle`
   - Actor: product
   - Action: `credit_agreement.settle`
   - Route: `POST /v1/credit-agreements/{creditAgreementId}/settle`, scopes credit_agreement:write, transfer:write, idempotency required, receipt yes
   - Body: none
   - Path: creditAgreementId
   - Query: none
   - Expect: none
   - Bindings: creditAgreementId <- plan.creditAgreementId

### Recover an overdue payment

- Journey id: `bnpl_payment_delinquency`
- Steps: 11

1. `sandbox_customer_onboard`
   - Actor: product
   - Action: platform write
   - Route: `POST /v1/sandbox/customer-onboarding`, scopes customer:write, idempotency required, receipt yes
   - Body: displayName, productId
   - Path: none
   - Query: none
   - Expect: none
   - Bindings: none
2. `sandbox_customer_access_activate`
   - Actor: product
   - Action: platform write
   - Route: `POST /v1/sandbox/customer-access/{customerAccessId}/activate`, scopes customer:write, idempotency required, receipt yes
   - Body: none
   - Path: customerAccessId
   - Query: none
   - Expect: none
   - Bindings: customerAccessId <- shopper.customerAccessId
3. `account_create`
   - Actor: product
   - Action: platform write
   - Route: `POST /v1/accounts`, scopes account:write, idempotency required, receipt yes
   - Body: currency, owner, role
   - Path: none
   - Query: none
   - Expect: none
   - Bindings: owner.id <- shopper.customerId
4. `sandbox_account_fund`
   - Actor: product
   - Action: platform write
   - Route: `POST /v1/sandbox/account-funding`, scopes transfer:write, idempotency required, receipt yes
   - Body: amount, currency, destinationAccountId
   - Path: none
   - Query: none
   - Expect: none
   - Bindings: destinationAccountId <- shopper_account.accountId
5. `account_create`
   - Actor: product
   - Action: platform write
   - Route: `POST /v1/accounts`, scopes account:write, idempotency required, receipt yes
   - Body: currency, owner, role
   - Path: none
   - Query: none
   - Expect: none
   - Bindings: owner.id <- shopper.customerId
6. `sandbox_account_fund`
   - Actor: product
   - Action: platform write
   - Route: `POST /v1/sandbox/account-funding`, scopes transfer:write, idempotency required, receipt yes
   - Body: amount, currency, destinationAccountId
   - Path: none
   - Query: none
   - Expect: none
   - Bindings: destinationAccountId <- pool_account.accountId
7. `credit_agreement_create`
   - Actor: product
   - Action: `credit_agreement.create`
   - Route: `POST /v1/credit-agreements`, scopes credit_agreement:write, idempotency required, receipt yes
   - Body: borrowerAccountId, currency, fundingAccountId, principal, productId
   - Path: none
   - Query: none
   - Expect: none
   - Bindings: borrowerAccountId <- shopper_account.accountId, fundingAccountId <- pool_account.accountId
8. `credit_agreement_advance`
   - Actor: product
   - Action: `credit_agreement.advance`
   - Route: `POST /v1/credit-agreements/{creditAgreementId}/advance`, scopes credit_agreement:write, transfer:write, idempotency required, receipt yes
   - Body: none
   - Path: creditAgreementId
   - Query: none
   - Expect: none
   - Bindings: creditAgreementId <- plan.creditAgreementId
9. `installment_create`
   - Actor: product
   - Action: `installment.create`
   - Route: `POST /v1/installments`, scopes installment:write, idempotency required, receipt yes
   - Body: agreementId, amount, borrowerAccountId, currency, dueAt, fundingAccountId, productId
   - Path: none
   - Query: none
   - Expect: none
   - Bindings: agreementId <- plan.creditAgreementId, borrowerAccountId <- shopper_account.accountId, fundingAccountId <- pool_account.accountId
10. `sandbox_platform_fire`
   - Actor: platform
   - Sandbox trigger: The platform fires `installment.mark_overdue` in normal lifecycle processing; sandbox fires it now.
   - Action: platform lifecycle trigger
   - Route: `POST /v1/sandbox/platform/fire`, scopes no scopes required, idempotency required, receipt yes
   - Body: input, operation
   - Path: none
   - Query: none
   - Expect: none
   - Bindings: input.installmentId <- payment.installmentId
11. `sandbox_platform_fire`
   - Actor: platform
   - Sandbox trigger: The platform fires `installment.collect` in normal lifecycle processing; sandbox fires it now.
   - Action: platform lifecycle trigger
   - Route: `POST /v1/sandbox/platform/fire`, scopes no scopes required, idempotency required, receipt yes
   - Body: input, operation
   - Path: none
   - Query: none
   - Expect: none
   - Bindings: input.installmentId <- payment.installmentId

### Hand off to collections

- Journey id: `bnpl_collections_handoff`
- Steps: 14

1. `sandbox_customer_onboard`
   - Actor: product
   - Action: platform write
   - Route: `POST /v1/sandbox/customer-onboarding`, scopes customer:write, idempotency required, receipt yes
   - Body: displayName, productId
   - Path: none
   - Query: none
   - Expect: none
   - Bindings: none
2. `sandbox_customer_access_activate`
   - Actor: product
   - Action: platform write
   - Route: `POST /v1/sandbox/customer-access/{customerAccessId}/activate`, scopes customer:write, idempotency required, receipt yes
   - Body: none
   - Path: customerAccessId
   - Query: none
   - Expect: none
   - Bindings: customerAccessId <- shopper.customerAccessId
3. `account_create`
   - Actor: product
   - Action: platform write
   - Route: `POST /v1/accounts`, scopes account:write, idempotency required, receipt yes
   - Body: currency, owner, role
   - Path: none
   - Query: none
   - Expect: none
   - Bindings: owner.id <- shopper.customerId
4. `sandbox_account_fund`
   - Actor: product
   - Action: platform write
   - Route: `POST /v1/sandbox/account-funding`, scopes transfer:write, idempotency required, receipt yes
   - Body: amount, currency, destinationAccountId
   - Path: none
   - Query: none
   - Expect: none
   - Bindings: destinationAccountId <- shopper_account.accountId
5. `account_create`
   - Actor: product
   - Action: platform write
   - Route: `POST /v1/accounts`, scopes account:write, idempotency required, receipt yes
   - Body: currency, owner, role
   - Path: none
   - Query: none
   - Expect: none
   - Bindings: owner.id <- shopper.customerId
6. `sandbox_account_fund`
   - Actor: product
   - Action: platform write
   - Route: `POST /v1/sandbox/account-funding`, scopes transfer:write, idempotency required, receipt yes
   - Body: amount, currency, destinationAccountId
   - Path: none
   - Query: none
   - Expect: none
   - Bindings: destinationAccountId <- pool_account.accountId
7. `credit_agreement_create`
   - Actor: product
   - Action: `credit_agreement.create`
   - Route: `POST /v1/credit-agreements`, scopes credit_agreement:write, idempotency required, receipt yes
   - Body: borrowerAccountId, currency, fundingAccountId, principal, productId
   - Path: none
   - Query: none
   - Expect: none
   - Bindings: borrowerAccountId <- shopper_account.accountId, fundingAccountId <- pool_account.accountId
8. `credit_agreement_advance`
   - Actor: product
   - Action: `credit_agreement.advance`
   - Route: `POST /v1/credit-agreements/{creditAgreementId}/advance`, scopes credit_agreement:write, transfer:write, idempotency required, receipt yes
   - Body: none
   - Path: creditAgreementId
   - Query: none
   - Expect: none
   - Bindings: creditAgreementId <- plan.creditAgreementId
9. `installment_create`
   - Actor: product
   - Action: `installment.create`
   - Route: `POST /v1/installments`, scopes installment:write, idempotency required, receipt yes
   - Body: agreementId, amount, borrowerAccountId, currency, dueAt, fundingAccountId, productId
   - Path: none
   - Query: none
   - Expect: none
   - Bindings: agreementId <- plan.creditAgreementId, borrowerAccountId <- shopper_account.accountId, fundingAccountId <- pool_account.accountId
10. `sandbox_platform_fire`
   - Actor: platform
   - Sandbox trigger: The platform fires `installment.mark_overdue` in normal lifecycle processing; sandbox fires it now.
   - Action: platform lifecycle trigger
   - Route: `POST /v1/sandbox/platform/fire`, scopes no scopes required, idempotency required, receipt yes
   - Body: input, operation
   - Path: none
   - Query: none
   - Expect: none
   - Bindings: input.installmentId <- payment.installmentId
11. `collections_case_create`
   - Actor: product
   - Action: `collections_case.create`
   - Route: `POST /v1/collections-cases`, scopes collections_case:write, idempotency required, receipt yes
   - Body: agreementId, allowedContactEndHour, allowedContactStartHour, currency, debtorAccountId, maxContactAttemptsPer30Days, memo, productId, recoveryAccountId, referredAmount, triggeringInstallmentId
   - Path: none
   - Query: none
   - Expect: none
   - Bindings: agreementId <- plan.creditAgreementId, triggeringInstallmentId <- payment.installmentId, debtorAccountId <- shopper_account.accountId, recoveryAccountId <- pool_account.accountId
12. `collections_case_assign`
   - Actor: product
   - Action: platform write
   - Route: `POST /v1/collections-cases/{collectionsCaseId}/assign`, scopes collections_case:write, idempotency required, receipt yes
   - Body: none
   - Path: collectionsCaseId
   - Query: none
   - Expect: none
   - Bindings: collectionsCaseId <- case.collectionsCaseId
13. `contact_attempt_create`
   - Actor: product
   - Action: `contact_attempt.create`
   - Route: `POST /v1/contact-attempts`, scopes contact_attempt:write, idempotency required, receipt yes
   - Body: attemptedAt, caseId, channel, note, outcome, productId
   - Path: none
   - Query: none
   - Expect: none
   - Bindings: caseId <- case.collectionsCaseId
14. `collections_case_recover`
   - Actor: product
   - Action: platform write
   - Route: `POST /v1/collections-cases/{collectionsCaseId}/recover`, scopes collections_case:write, transfer:write, idempotency required, receipt yes
   - Body: recoveredAmount
   - Path: collectionsCaseId
   - Query: none
   - Expect: none
   - Bindings: collectionsCaseId <- case.collectionsCaseId

### Approve a spending limit

- Journey id: `bnpl_spending_limit_cycle`
- Steps: 11

1. `sandbox_customer_onboard`
   - Actor: product
   - Action: platform write
   - Route: `POST /v1/sandbox/customer-onboarding`, scopes customer:write, idempotency required, receipt yes
   - Body: displayName, productId
   - Path: none
   - Query: none
   - Expect: none
   - Bindings: none
2. `sandbox_customer_access_activate`
   - Actor: product
   - Action: platform write
   - Route: `POST /v1/sandbox/customer-access/{customerAccessId}/activate`, scopes customer:write, idempotency required, receipt yes
   - Body: none
   - Path: customerAccessId
   - Query: none
   - Expect: none
   - Bindings: customerAccessId <- shopper.customerAccessId
3. `account_create`
   - Actor: product
   - Action: platform write
   - Route: `POST /v1/accounts`, scopes account:write, idempotency required, receipt yes
   - Body: currency, owner, role
   - Path: none
   - Query: none
   - Expect: none
   - Bindings: owner.id <- shopper.customerId
4. `sandbox_account_fund`
   - Actor: product
   - Action: platform write
   - Route: `POST /v1/sandbox/account-funding`, scopes transfer:write, idempotency required, receipt yes
   - Body: amount, currency, destinationAccountId
   - Path: none
   - Query: none
   - Expect: none
   - Bindings: destinationAccountId <- shopper_account.accountId
5. `account_create`
   - Actor: product
   - Action: platform write
   - Route: `POST /v1/accounts`, scopes account:write, idempotency required, receipt yes
   - Body: currency, owner, role
   - Path: none
   - Query: none
   - Expect: none
   - Bindings: owner.id <- shopper.customerId
6. `sandbox_account_fund`
   - Actor: product
   - Action: platform write
   - Route: `POST /v1/sandbox/account-funding`, scopes transfer:write, idempotency required, receipt yes
   - Body: amount, currency, destinationAccountId
   - Path: none
   - Query: none
   - Expect: none
   - Bindings: destinationAccountId <- pool_account.accountId
7. `exposure_arrangement_create`
   - Actor: product
   - Action: `exposure_arrangement.create`
   - Route: `POST /v1/exposure-arrangements`, scopes exposure_arrangement:write, idempotency required, receipt yes
   - Body: approvedAmountsCapAmount, borrowerAccountId, borrowerEntityId, currency, productId
   - Path: none
   - Query: none
   - Expect: none
   - Bindings: borrowerAccountId <- shopper_account.accountId
8. `exposure_arrangement_approve`
   - Actor: product
   - Action: platform write
   - Route: `POST /v1/exposure-arrangements/{exposureArrangementId}/approve`, scopes exposure_arrangement:write, idempotency required, receipt yes
   - Body: none
   - Path: exposureArrangementId
   - Query: none
   - Expect: none
   - Bindings: exposureArrangementId <- limit.exposureArrangementId
9. `credit_agreement_create`
   - Actor: product
   - Action: `credit_agreement.create`
   - Route: `POST /v1/credit-agreements`, scopes credit_agreement:write, idempotency required, receipt yes
   - Body: borrowerAccountId, currency, exposureArrangementId, fundingAccountId, principal, productId
   - Path: none
   - Query: none
   - Expect: none
   - Bindings: borrowerAccountId <- shopper_account.accountId, fundingAccountId <- pool_account.accountId, exposureArrangementId <- limit.exposureArrangementId
10. `credit_agreement_advance`
   - Actor: product
   - Action: `credit_agreement.advance`
   - Route: `POST /v1/credit-agreements/{creditAgreementId}/advance`, scopes credit_agreement:write, transfer:write, idempotency required, receipt yes
   - Body: none
   - Path: creditAgreementId
   - Query: none
   - Expect: none
   - Bindings: creditAgreementId <- plan.creditAgreementId
11. `credit_agreement_settle`
   - Actor: product
   - Action: `credit_agreement.settle`
   - Route: `POST /v1/credit-agreements/{creditAgreementId}/settle`, scopes credit_agreement:write, transfer:write, idempotency required, receipt yes
   - Body: none
   - Path: creditAgreementId
   - Query: none
   - Expect: none
   - Bindings: creditAgreementId <- plan.creditAgreementId

### Raise the funding pool

- Journey id: `bnpl_funding_round`
- Steps: 18

1. `sandbox_customer_onboard`
   - Actor: product
   - Action: platform write
   - Route: `POST /v1/sandbox/customer-onboarding`, scopes customer:write, idempotency required, receipt yes
   - Body: displayName, productId
   - Path: none
   - Query: none
   - Expect: none
   - Bindings: none
2. `sandbox_customer_access_activate`
   - Actor: product
   - Action: platform write
   - Route: `POST /v1/sandbox/customer-access/{customerAccessId}/activate`, scopes customer:write, idempotency required, receipt yes
   - Body: none
   - Path: customerAccessId
   - Query: none
   - Expect: none
   - Bindings: customerAccessId <- borrower.customerAccessId
3. `account_create`
   - Actor: product
   - Action: platform write
   - Route: `POST /v1/accounts`, scopes account:write, idempotency required, receipt yes
   - Body: currency, owner, role
   - Path: none
   - Query: none
   - Expect: none
   - Bindings: owner.id <- borrower.customerId
4. `sandbox_customer_onboard`
   - Actor: product
   - Action: platform write
   - Route: `POST /v1/sandbox/customer-onboarding`, scopes customer:write, idempotency required, receipt yes
   - Body: displayName, productId
   - Path: none
   - Query: none
   - Expect: none
   - Bindings: none
5. `sandbox_customer_access_activate`
   - Actor: product
   - Action: platform write
   - Route: `POST /v1/sandbox/customer-access/{customerAccessId}/activate`, scopes customer:write, idempotency required, receipt yes
   - Body: none
   - Path: customerAccessId
   - Query: none
   - Expect: none
   - Bindings: customerAccessId <- investor.customerAccessId
6. `account_create`
   - Actor: product
   - Action: platform write
   - Route: `POST /v1/accounts`, scopes account:write, idempotency required, receipt yes
   - Body: currency, owner, role
   - Path: none
   - Query: none
   - Expect: none
   - Bindings: owner.id <- investor.customerId
7. `sandbox_account_fund`
   - Actor: product
   - Action: platform write
   - Route: `POST /v1/sandbox/account-funding`, scopes transfer:write, idempotency required, receipt yes
   - Body: amount, currency, destinationAccountId
   - Path: none
   - Query: none
   - Expect: none
   - Bindings: destinationAccountId <- investor_account.accountId
8. `sandbox_customer_onboard`
   - Actor: product
   - Action: platform write
   - Route: `POST /v1/sandbox/customer-onboarding`, scopes customer:write, idempotency required, receipt yes
   - Body: displayName, productId
   - Path: none
   - Query: none
   - Expect: none
   - Bindings: none
9. `sandbox_customer_access_activate`
   - Actor: product
   - Action: platform write
   - Route: `POST /v1/sandbox/customer-access/{customerAccessId}/activate`, scopes customer:write, idempotency required, receipt yes
   - Body: none
   - Path: customerAccessId
   - Query: none
   - Expect: none
   - Bindings: customerAccessId <- second_investor.customerAccessId
10. `account_create`
   - Actor: product
   - Action: platform write
   - Route: `POST /v1/accounts`, scopes account:write, idempotency required, receipt yes
   - Body: currency, owner, role
   - Path: none
   - Query: none
   - Expect: none
   - Bindings: owner.id <- second_investor.customerId
11. `sandbox_account_fund`
   - Actor: product
   - Action: platform write
   - Route: `POST /v1/sandbox/account-funding`, scopes transfer:write, idempotency required, receipt yes
   - Body: amount, currency, destinationAccountId
   - Path: none
   - Query: none
   - Expect: none
   - Bindings: destinationAccountId <- second_investor_account.accountId
12. `funding_round_create`
   - Actor: product
   - Action: `funding_round.create`
   - Route: `POST /v1/funding-rounds`, scopes funding_round:write, idempotency required, receipt yes
   - Body: borrowerAccountId, closeBy, currency, memo, productId, targetAmount
   - Path: none
   - Query: none
   - Expect: none
   - Bindings: borrowerAccountId <- borrower_account.accountId
13. `funding_commitment_create`
   - Actor: product
   - Action: `funding_commitment.create`
   - Route: `POST /v1/funding-commitments`, scopes funding_commitment:write, transfer:write, idempotency required, receipt yes
   - Body: amount, committedAt, fundingRoundId, investorAccountId, productId
   - Path: none
   - Query: none
   - Expect: none
   - Bindings: fundingRoundId <- round.fundingRoundId, investorAccountId <- investor_account.accountId
14. `funding_commitment_create`
   - Actor: product
   - Action: `funding_commitment.create`
   - Route: `POST /v1/funding-commitments`, scopes funding_commitment:write, transfer:write, idempotency required, receipt yes
   - Body: amount, committedAt, fundingRoundId, investorAccountId, productId
   - Path: none
   - Query: none
   - Expect: none
   - Bindings: fundingRoundId <- round.fundingRoundId, investorAccountId <- second_investor_account.accountId
15. `sandbox_platform_fire`
   - Actor: platform
   - Sandbox trigger: The platform fires `funding_round.activate` in normal lifecycle processing; sandbox fires it now.
   - Action: platform lifecycle trigger
   - Route: `POST /v1/sandbox/platform/fire`, scopes no scopes required, idempotency required, receipt yes
   - Body: input, operation
   - Path: none
   - Query: none
   - Expect: none
   - Bindings: input.fundingRoundId <- round.fundingRoundId
16. `sandbox_platform_fire`
   - Actor: platform
   - Sandbox trigger: The platform fires `funding_commitment.collect` in normal lifecycle processing; sandbox fires it now.
   - Action: platform lifecycle trigger
   - Route: `POST /v1/sandbox/platform/fire`, scopes no scopes required, idempotency required, receipt yes
   - Body: input, operation
   - Path: none
   - Query: none
   - Expect: none
   - Bindings: input.fundingCommitmentId <- first_commitment.fundingCommitmentId
17. `sandbox_platform_fire`
   - Actor: platform
   - Sandbox trigger: The platform fires `funding_commitment.collect` in normal lifecycle processing; sandbox fires it now.
   - Action: platform lifecycle trigger
   - Route: `POST /v1/sandbox/platform/fire`, scopes no scopes required, idempotency required, receipt yes
   - Body: input, operation
   - Path: none
   - Query: none
   - Expect: none
   - Bindings: input.fundingCommitmentId <- second_commitment.fundingCommitmentId
18. `funding_round_close`
   - Actor: product
   - Action: platform write
   - Route: `POST /v1/funding-rounds/{fundingRoundId}/close`, scopes funding_round:write, idempotency required, receipt yes
   - Body: none
   - Path: fundingRoundId
   - Query: none
   - Expect: none
   - Bindings: fundingRoundId <- round.fundingRoundId

### Onboard a shopper

- Journey id: `bnpl_shopper_onboarding`
- Steps: 2

1. `sandbox_customer_onboard`
   - Actor: product
   - Action: platform write
   - Route: `POST /v1/sandbox/customer-onboarding`, scopes customer:write, idempotency required, receipt yes
   - Body: displayName, productId
   - Path: none
   - Query: none
   - Expect: none
   - Bindings: none
2. `sandbox_customer_access_activate`
   - Actor: product
   - Action: platform write
   - Route: `POST /v1/sandbox/customer-access/{customerAccessId}/activate`, scopes customer:write, idempotency required, receipt yes
   - Body: none
   - Path: customerAccessId
   - Query: none
   - Expect: none
   - Bindings: customerAccessId <- shopper.customerAccessId

### Move principal funds

- Journey id: `bnpl_principal_movement`
- Steps: 7

1. `sandbox_customer_onboard`
   - Actor: product
   - Action: platform write
   - Route: `POST /v1/sandbox/customer-onboarding`, scopes customer:write, idempotency required, receipt yes
   - Body: displayName, productId
   - Path: none
   - Query: none
   - Expect: none
   - Bindings: none
2. `sandbox_customer_access_activate`
   - Actor: product
   - Action: platform write
   - Route: `POST /v1/sandbox/customer-access/{customerAccessId}/activate`, scopes customer:write, idempotency required, receipt yes
   - Body: none
   - Path: customerAccessId
   - Query: none
   - Expect: none
   - Bindings: customerAccessId <- source.customerAccessId
3. `account_create`
   - Actor: product
   - Action: platform write
   - Route: `POST /v1/accounts`, scopes account:write, idempotency required, receipt yes
   - Body: currency, owner, role
   - Path: none
   - Query: none
   - Expect: none
   - Bindings: owner.id <- source.customerId
4. `sandbox_account_fund`
   - Actor: product
   - Action: platform write
   - Route: `POST /v1/sandbox/account-funding`, scopes transfer:write, idempotency required, receipt yes
   - Body: amount, currency, destinationAccountId
   - Path: none
   - Query: none
   - Expect: none
   - Bindings: destinationAccountId <- source_account.accountId
5. `account_create`
   - Actor: product
   - Action: platform write
   - Route: `POST /v1/accounts`, scopes account:write, idempotency required, receipt yes
   - Body: currency, owner, role
   - Path: none
   - Query: none
   - Expect: none
   - Bindings: owner.id <- source.customerId
6. `internal_transfer_reserve`
   - Actor: product
   - Action: platform write
   - Route: `POST /v1/internal-transfers/reserve`, scopes transfer:write, idempotency required, receipt yes
   - Body: amount, currency, destinationAccountId, memo, sourceAccountId
   - Path: none
   - Query: none
   - Expect: none
   - Bindings: sourceAccountId <- source_account.accountId, destinationAccountId <- destination_account.accountId
7. `internal_transfer_post`
   - Actor: product
   - Action: platform write
   - Route: `POST /v1/internal-transfers/{transferId}/post`, scopes transfer:write, idempotency required, receipt yes
   - Body: none
   - Path: transferId
   - Query: none
   - Expect: none
   - Bindings: transferId <- principal.transferId

## Cost

Cost table version 2026-09-02.1. Flat amounts are multiplied by the action count; basis points price the meter's moved amount, which an action count does not determine.

| Instrument | Action | Effect | Meter | Flat per action | Rate on meter | 1,000 per month | 100,000 per month | 10,000,000 per month |
| --- | --- | --- | --- | --- | --- | --- | --- | --- |
| `collections_case` | `agree_plan` | `decides.collections_agency` | `instrument.event.count` | 0.00 SAR | 0 bps | 0.00 SAR | 0.00 SAR | 0.00 SAR |
| `collections_case` | `assign` | `decides.collections_agency` | `instrument.event.count` | 0.00 SAR | 0 bps | 0.00 SAR | 0.00 SAR | 0.00 SAR |
| `collections_case` | `create` | `reads.requires_refs` | `instrument.event.count` | 0.00 SAR | 0 bps | 0.00 SAR | 0.00 SAR | 0.00 SAR |
| `collections_case` | `recover` | `moves.transfer.internal` | `transfer.internal.volume_sar` | 0.75 SAR | 11 bps | 750.00 SAR | 75,000.00 SAR | 7,500,000.00 SAR |
| `collections_case` | `recover` | `reads.requires_refs` | `instrument.event.count` | 0.00 SAR | 0 bps | 0.00 SAR | 0.00 SAR | 0.00 SAR |
| `contact_attempt` | `create` | `reads.requires_refs` | `instrument.event.count` | 0.00 SAR | 0 bps | 0.00 SAR | 0.00 SAR | 0.00 SAR |
| `credit_agreement` | `advance` | `decides.credit_servicing` | `instrument.event.count` | 0.00 SAR | 0 bps | 0.00 SAR | 0.00 SAR | 0.00 SAR |
| `credit_agreement` | `advance` | `moves.transfer.internal` | `transfer.internal.volume_sar` | 0.75 SAR | 11 bps | 750.00 SAR | 75,000.00 SAR | 7,500,000.00 SAR |
| `credit_agreement` | `advance` | `reads.requires_refs` | `instrument.event.count` | 0.00 SAR | 0 bps | 0.00 SAR | 0.00 SAR | 0.00 SAR |
| `credit_agreement` | `create` | `reads.requires_refs` | `instrument.event.count` | 0.00 SAR | 0 bps | 0.00 SAR | 0.00 SAR | 0.00 SAR |
| `credit_agreement` | `refinance` | `decides.credit_servicing` | `instrument.event.count` | 0.00 SAR | 0 bps | 0.00 SAR | 0.00 SAR | 0.00 SAR |
| `credit_agreement` | `reschedule` | `decides.credit_servicing` | `instrument.event.count` | 0.00 SAR | 0 bps | 0.00 SAR | 0.00 SAR | 0.00 SAR |
| `credit_agreement` | `settle` | `moves.transfer.internal` | `transfer.internal.volume_sar` | 0.75 SAR | 11 bps | 750.00 SAR | 75,000.00 SAR | 7,500,000.00 SAR |
| `funding_commitment` | `collect` | `moves.transfer.internal` | `transfer.internal.volume_sar` | 0.75 SAR | 11 bps | 750.00 SAR | 75,000.00 SAR | 7,500,000.00 SAR |
| `funding_commitment` | `collect` | `schedules.due` | `instrument.event.count` | 0.00 SAR | 0 bps | 0.00 SAR | 0.00 SAR | 0.00 SAR |
| `funding_commitment` | `create` | `moves.transfer.internal` | `transfer.internal.volume_sar` | 0.75 SAR | 11 bps | 750.00 SAR | 75,000.00 SAR | 7,500,000.00 SAR |
| `funding_commitment` | `create` | `reads.requires_refs` | `instrument.event.count` | 0.00 SAR | 0 bps | 0.00 SAR | 0.00 SAR | 0.00 SAR |
| `funding_commitment` | `refund` | `moves.transfer.internal` | `transfer.internal.volume_sar` | 0.75 SAR | 11 bps | 750.00 SAR | 75,000.00 SAR | 7,500,000.00 SAR |
| `funding_commitment` | `refund` | `schedules.due` | `instrument.event.count` | 0.00 SAR | 0 bps | 0.00 SAR | 0.00 SAR | 0.00 SAR |
| `funding_round` | `activate` | `reads.requires_aggregate` | `instrument.event.count` | 0.00 SAR | 0 bps | 0.00 SAR | 0.00 SAR | 0.00 SAR |
| `funding_round` | `activate` | `schedules.due` | `instrument.event.count` | 0.00 SAR | 0 bps | 0.00 SAR | 0.00 SAR | 0.00 SAR |
| `funding_round` | `close` | `reads.requires_aggregate` | `instrument.event.count` | 0.00 SAR | 0 bps | 0.00 SAR | 0.00 SAR | 0.00 SAR |
| `funding_round` | `fail` | `reads.requires_aggregate` | `instrument.event.count` | 0.00 SAR | 0 bps | 0.00 SAR | 0.00 SAR | 0.00 SAR |
| `funding_round` | `fail` | `schedules.due` | `instrument.event.count` | 0.00 SAR | 0 bps | 0.00 SAR | 0.00 SAR | 0.00 SAR |
| `installment` | `collect` | `moves.transfer.internal` | `transfer.internal.volume_sar` | 0.75 SAR | 11 bps | 750.00 SAR | 75,000.00 SAR | 7,500,000.00 SAR |
| `installment` | `collect` | `reads.requires_aggregate` | `instrument.event.count` | 0.00 SAR | 0 bps | 0.00 SAR | 0.00 SAR | 0.00 SAR |
| `installment` | `collect` | `reads.requires_refs` | `instrument.event.count` | 0.00 SAR | 0 bps | 0.00 SAR | 0.00 SAR | 0.00 SAR |
| `installment` | `collect` | `schedules.due` | `instrument.event.count` | 0.00 SAR | 0 bps | 0.00 SAR | 0.00 SAR | 0.00 SAR |
| `installment` | `mark_overdue` | `schedules.due` | `instrument.event.count` | 0.00 SAR | 0 bps | 0.00 SAR | 0.00 SAR | 0.00 SAR |
| `installment` | `write_off` | `reads.requires_aggregate` | `instrument.event.count` | 0.00 SAR | 0 bps | 0.00 SAR | 0.00 SAR | 0.00 SAR |
| **Total flat** | | | | | | **5,250.00 SAR** | **525,000.00 SAR** | **52,500,000.00 SAR** |
