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

# Closed-Loop Wallet

> The frozen Closed-Loop Wallet Product Build, version 1.

- Composition id: `closed_loop_wallet`
- Version: 1
- Digest: `3007dc7dc7d0d208c6a947ff6cc852acbfc7482e204819dd216e33b9ab7124b3`
- Currencies: `SAR`
- Webhook events: 28

## Instruments

| Instrument | Title | States | Actions |
| --- | --- | --- | --- |
| [`wallet`](../instruments/wallet.md) | Wallet | 4 | activate, create, freeze, topup, withdraw |
| [`wallet_spend`](../instruments/wallet_spend.md) | Wallet spend | 3 | create, post, refund |

## Journeys

### Run an active wallet cycle

- Journey id: `wallet_active_cycle`
- Steps: 9

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. `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 <- holder_account.accountId
5. `wallet_create`
   - Actor: product
   - Action: `wallet.create`
   - Route: `POST /v1/wallets`, scopes wallet:write, idempotency required, receipt yes
   - Body: currency, holderAccountId, limitAmount, productId
   - Path: none
   - Query: none
   - Expect: none
   - Bindings: holderAccountId <- holder_account.accountId
6. `wallet_activate`
   - Actor: product
   - Action: `wallet.activate`
   - Route: `POST /v1/wallets/{walletId}/activate`, scopes wallet:write, idempotency required, receipt yes
   - Body: none
   - Path: walletId
   - Query: none
   - Expect: none
   - Bindings: walletId <- wallet.walletId
7. `wallet_topup`
   - Actor: product
   - Action: `wallet.topup`
   - Route: `POST /v1/wallets/{walletId}/topup`, scopes transfer:write, wallet:write, idempotency required, receipt yes
   - Body: amount
   - Path: walletId
   - Query: none
   - Expect: none
   - Bindings: walletId <- wallet.walletId
8. `wallet_withdraw`
   - Actor: product
   - Action: `wallet.withdraw`
   - Route: `POST /v1/wallets/{walletId}/withdraw`, scopes transfer:write, wallet:write, idempotency required, receipt yes
   - Body: amount
   - Path: walletId
   - Query: none
   - Expect: none
   - Bindings: walletId <- wallet.walletId
9. `wallet_close`
   - Actor: product
   - Action: platform write
   - Route: `POST /v1/wallets/{walletId}/close`, scopes wallet:write, idempotency required, receipt yes
   - Body: none
   - Path: walletId
   - Query: none
   - Expect: none
   - Bindings: walletId <- wallet.walletId

### Refund a wallet spend

- Journey id: `refund_wallet_spend`
- 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 <- 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 <- holder_account.accountId
6. `wallet_create`
   - Actor: product
   - Action: `wallet.create`
   - Route: `POST /v1/wallets`, scopes wallet:write, idempotency required, receipt yes
   - Body: currency, holderAccountId, limitAmount, productId
   - Path: none
   - Query: none
   - Expect: none
   - Bindings: holderAccountId <- holder_account.accountId
7. `wallet_activate`
   - Actor: product
   - Action: `wallet.activate`
   - Route: `POST /v1/wallets/{walletId}/activate`, scopes wallet:write, idempotency required, receipt yes
   - Body: none
   - Path: walletId
   - Query: none
   - Expect: none
   - Bindings: walletId <- wallet.walletId
8. `wallet_topup`
   - Actor: product
   - Action: `wallet.topup`
   - Route: `POST /v1/wallets/{walletId}/topup`, scopes transfer:write, wallet:write, idempotency required, receipt yes
   - Body: amount
   - Path: walletId
   - Query: none
   - Expect: none
   - Bindings: walletId <- wallet.walletId
9. `wallet_spend_create`
   - Actor: product
   - Action: `wallet_spend.create`
   - Route: `POST /v1/wallet-spends`, scopes wallet_spend:write, idempotency required, receipt yes
   - Body: amount, beneficiaryAccountId, productId, walletId
   - Path: none
   - Query: none
   - Expect: none
   - Bindings: beneficiaryAccountId <- beneficiary_account.accountId, walletId <- wallet.walletId
10. `wallet_spend_post`
   - Actor: product
   - Action: `wallet_spend.post`
   - Route: `POST /v1/wallet-spends/{walletSpendId}/post`, scopes transfer:write, wallet_spend:write, idempotency required, receipt yes
   - Body: none
   - Path: walletSpendId
   - Query: none
   - Expect: none
   - Bindings: walletSpendId <- spend.walletSpendId
11. `wallet_spend_refund`
   - Actor: product
   - Action: `wallet_spend.refund`
   - Route: `POST /v1/wallet-spends/{walletSpendId}/refund`, scopes transfer:write, wallet_spend:write, idempotency required, receipt yes
   - Body: none
   - Path: walletSpendId
   - Query: none
   - Expect: none
   - Bindings: walletSpendId <- spend.walletSpendId

### Load and spend a balance

- Journey id: `wallet_load_spend_cycle`
- Steps: 13

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 <- member.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 <- member.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 <- member_account.accountId
5. `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
6. `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 <- merchant.customerAccessId
7. `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 <- merchant.customerId
8. `wallet_create`
   - Actor: product
   - Action: `wallet.create`
   - Route: `POST /v1/wallets`, scopes wallet:write, idempotency required, receipt yes
   - Body: currency, holderAccountId, limitAmount, productId
   - Path: none
   - Query: none
   - Expect: none
   - Bindings: holderAccountId <- member_account.accountId
9. `wallet_activate`
   - Actor: product
   - Action: `wallet.activate`
   - Route: `POST /v1/wallets/{walletId}/activate`, scopes wallet:write, idempotency required, receipt yes
   - Body: none
   - Path: walletId
   - Query: none
   - Expect: none
   - Bindings: walletId <- wallet.walletId
10. `wallet_topup`
   - Actor: product
   - Action: `wallet.topup`
   - Route: `POST /v1/wallets/{walletId}/topup`, scopes transfer:write, wallet:write, idempotency required, receipt yes
   - Body: amount
   - Path: walletId
   - Query: none
   - Expect: none
   - Bindings: walletId <- wallet.walletId
11. `wallet_spend_create`
   - Actor: product
   - Action: `wallet_spend.create`
   - Route: `POST /v1/wallet-spends`, scopes wallet_spend:write, idempotency required, receipt yes
   - Body: amount, beneficiaryAccountId, productId, walletId
   - Path: none
   - Query: none
   - Expect: none
   - Bindings: walletId <- wallet.walletId, beneficiaryAccountId <- merchant_account.accountId
12. `wallet_spend_post`
   - Actor: product
   - Action: `wallet_spend.post`
   - Route: `POST /v1/wallet-spends/{walletSpendId}/post`, scopes transfer:write, wallet_spend:write, idempotency required, receipt yes
   - Body: none
   - Path: walletSpendId
   - Query: none
   - Expect: none
   - Bindings: walletSpendId <- spend.walletSpendId
13. `wallet_withdraw`
   - Actor: product
   - Action: `wallet.withdraw`
   - Route: `POST /v1/wallets/{walletId}/withdraw`, scopes transfer:write, wallet:write, idempotency required, receipt yes
   - Body: amount
   - Path: walletId
   - Query: none
   - Expect: none
   - Bindings: walletId <- wallet.walletId

### Reload a balance

- Journey id: `wallet_reload_cycle`
- Steps: 8

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 <- member.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 <- member.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 <- member_account.accountId
5. `wallet_create`
   - Actor: product
   - Action: `wallet.create`
   - Route: `POST /v1/wallets`, scopes wallet:write, idempotency required, receipt yes
   - Body: currency, holderAccountId, limitAmount, productId
   - Path: none
   - Query: none
   - Expect: none
   - Bindings: holderAccountId <- member_account.accountId
6. `wallet_activate`
   - Actor: product
   - Action: `wallet.activate`
   - Route: `POST /v1/wallets/{walletId}/activate`, scopes wallet:write, idempotency required, receipt yes
   - Body: none
   - Path: walletId
   - Query: none
   - Expect: none
   - Bindings: walletId <- wallet.walletId
7. `wallet_topup`
   - Actor: product
   - Action: `wallet.topup`
   - Route: `POST /v1/wallets/{walletId}/topup`, scopes transfer:write, wallet:write, idempotency required, receipt yes
   - Body: amount
   - Path: walletId
   - Query: none
   - Expect: none
   - Bindings: walletId <- wallet.walletId
8. `wallet_withdraw`
   - Actor: product
   - Action: `wallet.withdraw`
   - Route: `POST /v1/wallets/{walletId}/withdraw`, scopes transfer:write, wallet:write, idempotency required, receipt yes
   - Body: amount
   - Path: walletId
   - Query: none
   - Expect: none
   - Bindings: walletId <- wallet.walletId

### Onboard a member

- Journey id: `wallet_member_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 <- member.customerAccessId

### Move load funds

- Journey id: `wallet_load_movement`
- Steps: 9

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 <- payer.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 <- payer.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 <- payer_account.accountId
5. `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
6. `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 <- payee.customerAccessId
7. `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 <- payee.customerId
8. `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 <- payer_account.accountId, destinationAccountId <- payee_account.accountId
9. `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 <- load.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 |
| --- | --- | --- | --- | --- | --- | --- | --- | --- |
| `wallet` | `topup` | `moves.transfer.internal` | `transfer.internal.volume_sar` | 0.75 SAR | 11 bps | 750.00 SAR | 75,000.00 SAR | 7,500,000.00 SAR |
| `wallet` | `withdraw` | `moves.transfer.internal` | `transfer.internal.volume_sar` | 0.75 SAR | 11 bps | 750.00 SAR | 75,000.00 SAR | 7,500,000.00 SAR |
| `wallet_spend` | `create` | `reads.requires_refs` | `instrument.event.count` | 0.00 SAR | 0 bps | 0.00 SAR | 0.00 SAR | 0.00 SAR |
| `wallet_spend` | `post` | `moves.transfer.internal` | `transfer.internal.volume_sar` | 0.75 SAR | 11 bps | 750.00 SAR | 75,000.00 SAR | 7,500,000.00 SAR |
| `wallet_spend` | `post` | `reads.requires_refs` | `instrument.event.count` | 0.00 SAR | 0 bps | 0.00 SAR | 0.00 SAR | 0.00 SAR |
| `wallet_spend` | `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 |
| **Total flat** | | | | | | **3,000.00 SAR** | **300,000.00 SAR** | **30,000,000.00 SAR** |
