> ## Documentation Index
> Fetch the complete documentation index at: https://agents-docs.lightspark.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Trusted beneficiaries

> Whitelist a payee so future sends skip the per-transaction ceremony

> [!NOTE]
>
> Applies only to customers in an SCA-required region (EU). Every endpoint here
> returns **`409`** for other customers.

Trusting a beneficiary is a one-time, SCA-gated step that whitelists an external
account. Only **USDC addresses** can be trusted today. Once trusted, future sends
to that payee are no longer dynamically linked; they drop to a lighter
authentication instead of a full per-transaction challenge. Use it for recurring
payouts to known destinations.

The beneficiary is identified end-to-end by its **`externalAccountId`** in the
path, so there is no separate whitelist handle to track. All paths below are
relative to `https://api.lightspark.com/grid/2025-10-13`.

## Trusting a beneficiary

**1. Start the trust**

```bash theme={null}
POST /customers/external-accounts/{externalAccountId}/trust
```

Returns the `scaChallenge` to satisfy:

```json theme={null}
{ "scaChallenge": { "id": "…", "factor": "SMS_OTP", "expiresAt": "2025-10-03T12:05:00Z", "availableFactors": ["SMS_OTP"] } }
```

> [!NOTE]
>
> `scaChallenge` may be **omitted** when no challenge is issued. In that case,
> confirm directly without a `challengeId`.

**2. Confirm the trust**

Submit the proof for the factor Grid returned in `scaChallenge`: `code` for
`SMS_OTP` / `TOTP`, or `passkeyAssertion` + `origin` for `PASSKEY`. Echo
`challengeId` when start issued one. Do not assume a factor is available unless
it appears in `scaChallenge.availableFactors`.

```bash theme={null}
POST /customers/external-accounts/{externalAccountId}/trust/confirm

{ "challengeId": "…", "code": "000000" }
```

```json theme={null}
{ "trusted": true }
```

An invalid or expired proof returns `400`. **In sandbox, the code is always
`000000`.**

## Untrusting a beneficiary

Untrusting mirrors trusting: a start call issues the challenge, then confirm
submits the proof.

**1. Start the untrust**

```bash theme={null}
POST /customers/external-accounts/{externalAccountId}/untrust
```

Returns the `scaChallenge` to satisfy, omitted when no challenge is issued (the
caller then confirms without a `challengeId`).

**2. Confirm the untrust**

Submit the proof for the factor Grid returned in `scaChallenge`: `code` for
`SMS_OTP` / `TOTP`, or `passkeyAssertion` + `origin` for `PASSKEY`. Echo
`challengeId` when start issued one. Returns `trusted: false`.

```bash theme={null}
POST /customers/external-accounts/{externalAccountId}/untrust/confirm

{ "challengeId": "…", "code": "000000" }
```

Once untrusted, sends to that beneficiary are dynamically linked again and each
one requires a full per-transaction challenge.

## Deleting a trusted account

A trusted external account can't be deleted while the trust is in place:
`DELETE /customers/external-accounts/{externalAccountId}` returns
**`409` `BENEFICIARY_TRUSTED`**. Untrust first (the SCA-gated flow above), then
delete. Trust is never revoked as a side effect of a delete — untrusting
requires the customer's SCA challenge, which a `DELETE` has no way to carry.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.