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

# Settlement webhooks and testing

> Act on each settlement webhook event, and rehearse settlement in sandbox before real money moves.

Subscribe to the settlement events below and treat each one as a prompt to read the resource. `GET /v2/partners/config` reports the event catalog and delivery policy for your deployment under `capabilities.webhooks`; read it at startup.

## Act on these events

| Event | What to do |
| - | - |
| `payment.accepted` | Funds were verified. Settlement may still be pending. Do not treat this as settled |
| `payment.succeeded` | Settlement completed. Release the obligation and close the record |
| `payment.settlement_returned` | A broadcast settlement was returned or rejected. Hold any credit you were about to give. There is no automatic replacement |
| `payment.requires_intervention` | Automation stopped. Put the payment in front of a named person. Read `operationalReasonCode` |
| `deposit.settled` | A deposit reached the destination. This is the completed-deposit event |
| `deposit.reversed` | An accounting record was invalidated. Reverse your own credit. **This is not an on-chain clawback** |
| `deposit.requires_intervention` | The deposit needs manual review |
| `account.updated` | Account configuration changed, which includes settlement configuration. Fetch the account |

<Note>
  Intermediate settlement worker states are internal and are never delivered to partner endpoints. If you need finer granularity than these events give you, poll the payment or the deposit instead.
</Note>

## Handle every event the same way

<Steps>
  <Step title="Verify, then dedupe">
    Verify the signature over the raw body, then deduplicate on the event id under a unique constraint in the same transaction as your business update.
  </Step>

  <Step title="Read the resource">
    Derive your effect from a fresh `GET`, not from the payload. A duplicate or out-of-order delivery then converges on the same answer instead of corrupting it.
  </Step>

  <Step title="Return fast, process after">
    Return a `2xx` quickly and do the work asynchronously.
  </Step>

  <Step title="Alert on abandonment">
    A failed delivery retries. An abandoned one does not, and only helps if someone is watching.
  </Step>
</Steps>

An endpoint with an empty `subscribedEvents` receives every public partner event for the environment. Narrow it deliberately, and make your consumer ignore unknown event names rather than throwing, because new events are added over time.

## Rehearse these cases in sandbox

Sandbox runs the same verification, state machine and reconciliation as production. Start by confirming the account can settle at all: a `null` profile means it cannot be named as a payment recipient or issued a deposit address.

```bash theme={null}
curl https://staging-api-v2.stableyard.fi/v2/accounts/acct_123/settlement-profile \
  -u "$APP_ID:$APP_SECRET"
```

| Rehearse | Pass condition |
| - | - |
| Set a settlement destination, then collect | The payment reaches `succeeded` and a settlement transaction exists |
| Collect against an account with no destination | `settlement_profile_required`, and your code recovers by setting one |
| Change the destination after a payment exists | The in-flight payment settles to the snapshot, not the new destination |
| Try to disable the preferred destination | Refused, and your settings screen makes the user choose a replacement first |
| Observe `payment.accepted` alone | Nothing fulfils and nothing reconciles as settled |
| Drive a payment into `requires_intervention` | Your order goes on hold, an alert fires, and the financial status does not regress |
| Deliver the same settlement event twice | Exactly one business update |
| Reconcile a settlement from your own `externalReference` alone | Someone who did not write the integration can find it |

Run each case against the state your production code will actually be in. A case that passes because you were watching the database is not a pass.

## Some paths cannot be rehearsed

| Not rehearsable | Why |
| - | - |
| A production destination | Sandbox uses test networks. Chain ids differ, and an address valid in one is not valid in the other. Never carry a chain id or a destination across |
| Settlement to a bank or rail destination | Neither can receive settlement in any environment today. They can be linked and listed, and nothing selects them |
| Resolving an intervention | Observing the state is rehearsable. Clearing it is a Stableyard operations action |
| A corridor with no sandbox path | Some corridors have no rehearsal outside production. Plan a supervised low-value transfer, not a launch |
| Your production entitlements | Capabilities are granted per environment. Read `GET /v2/partners/config` with the production credential before assuming anything carried across |

[Environments](/environments) covers what else is isolated and what to prove before you switch a base URL.

<Card title="Next: Webhooks" icon="bell" href="/webhooks">
  The full event catalog, signature verification and retries.
</Card>


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