A Trusted Beneficiary is a whitelisted IBAN or USDC address the user has marked as known via a one-time SCA confirmation. Subsequent debits to that destination still require SCA, but the Dynamic Linking comparison is skipped (the hash is still computed and stored for audit). This is the carve-out that enables TOTP on EUR / USDC debits and makes recurring payouts ergonomic.
Trust is a per-destination, per-user mark - not an account-level switch. Untrust is symmetric and requires the same SCA flow.
| Operation | Endpoint |
|---|---|
| Mark an IBAN trusted | POST /wallets/beneficiary/iban/trust |
| Mark an IBAN untrusted | POST /wallets/beneficiary/iban/untrust |
| Mark an address trusted | POST /wallets/beneficiary/address/trust |
| Mark an address untrusted | POST /wallets/beneficiary/address/untrust |
| Confirm any trust action | POST /wallets/beneficiary/confirm-trust |
| Confirm any untrust action | POST /wallets/beneficiary/confirm-untrust |
Only USDC-family addresses are eligible for the address-side trust: USDC, USDC_POLYGON, USDC_SOL. Other currencies return 30075. IBAN trust is always EUR / SEPA.
Initiate
POST /wallets/beneficiary/iban/trust
Authorization: <HMAC>
{
"userId": "<uuid>",
"whitelistedIbanId": "<uuid>",
"verificationMethod": "sms" | "totp" | "passkey"
}Returns { verificationMethod, ...challenge }. For SMS, challenge carries { ok: true, challengeId, ... }. For passkey, the WebAuthn assertion request. For TOTP, { ok: true }.
The address-side endpoint takes whitelistedAddressId instead of whitelistedIbanId and otherwise follows the same shape. /iban/untrust and /address/untrust are identical to the corresponding trust endpoints — same body, same return shape — and route to the confirm-untrust endpoint.
Confirm
The same confirm endpoint handles both IBAN and address; the route is selected by whichever of whitelistedIbanId or whitelistedAddressId is present.
POST /wallets/beneficiary/confirm-trust
Authorization: <HMAC>
{
"userId": "<uuid>",
// Exactly one of:
"whitelistedIbanId": "<uuid>",
"whitelistedAddressId": "<uuid>",
"verificationMethod": "sms" | "totp" | "passkey",
// SMS:
"challengeId": "<uuid>",
"code": "123456",
// TOTP:
"code": "123456",
// Passkey:
"origin": "https://app.example.com",
"credential": { /* PublicKeyCredential serialised */ }
}Returns { ok: true }. The USDC carve-out is re-checked at confirm time, so an address that lost its ACTIVATED status between initiate and confirm fails with 30073. /confirm-untrust has the same shape and the same return.
Standing orders
A standing order is a recurring debit. Striga cannot prompt the user for SCA on every recurrence, so the recurring authorisation is captured once at setup time - this is envelope SCA. The factor policy is permissive (SMS, TOTP, passkey all allowed regardless of source currency) because there is no per-execution Dynamic Linking to constrain.
To keep PSD2 happy, the payee on a recurring debit must already be a Trusted Beneficiary:
- USDC standing orders require the destination address to be pre-trusted, or
64009. CRYPTO_TO_FIATstanding orders require the destination IBAN to be pre-trusted, or64011.
Walk the user through the trust flow first, then create the standing order.
POST /standing-orders/create
Authorization: <HMAC>
{
"userId": "<uuid>",
/* standing-order fields: schedule, source, destination, amount, … */
"verificationMethod": "sms" | "totp" | "passkey"
}If the factor requires a server-issued challenge (SMS or passkey), the response carries a challengeId. Confirm with:
POST /standing-orders/confirm
Authorization: <HMAC>
{
"userId": "<uuid>",
"standingOrderId": "<uuid>",
"verificationMethod": "sms" | "totp" | "passkey",
/* + the factor-specific fields shown above for /transaction/confirm */
}Cancellation and SMS resend follow the same shape:
| Endpoint | Purpose |
|---|---|
POST /standing-orders/create | Create, with envelope SCA. |
POST /standing-orders/confirm | Confirm setup or cancel. |
POST /standing-orders/cancel | Cancel an active standing order (SCA-confirmed). |
POST /standing-orders/resend-otp | SMS only; passkey returns 31100. |

