How to Use Chargeback Simulation
What This Is For
Chargeback Simulation lets you trigger dispute-like webhook events without creating real disputes at Stripe, Braintree, or Adyen.
Use it when you want to validate:
- dispute handler logic,
- webhook retries,
- normalized payload shape,
- Backoffice visibility,
- end-to-end QA flows in sandbox or staging.
This guide is the workflow doc on top of the raw API reference. It shows:
- how to trigger a simulation,
- how to do it through the SDK,
- how to wire a test callback,
- where to inspect results in Backoffice.
The Fast Mental Model
A simulation is not a fake UI-only object. It goes through the real webhook pipeline:
- You create a simulation.
- Syllecta generates provider-shaped dispute payloads.
- Those payloads go through the normal webhook log/forward path.
- Your callback receives normalized events.
- Backoffice shows the simulation row and the emitted webhook events.
That is why this feature is useful for QA: it exercises the same delivery path as real webhook traffic.
Prerequisites
You need:
- a tenant API key,
- Backoffice access for the same tenant,
- a callback URL that can receive JSON
POSTs, - a decision on which provider shape you want first:
stripebraintreeadyen
Before the first simulation, make sure tenant webhook configuration is already in place:
- callback URL is set,
- provider secret is set where the provider requires one.
If you have not done that yet, first read:
Supported Input
POST /v1/simulate/chargeback
Request body:
| Field | Type | Required | Notes |
|---|---|---|---|
provider | stripe | braintree | adyen | Yes | Chooses the generated provider-like payload shape. |
amount | integer | Yes | Minor units, for example cents. |
currency | string | Yes | Lowercase ISO code such as usd or eur. |
transactionId | string | No | External charge / transaction reference to echo in the payload. |
simulateLifecycle | boolean | No | Defaults to true. If false, only the initial event is emitted. |
finalStatus | won | lost | No | Valid only when simulateLifecycle=true. |
Lifecycle Behavior
When simulateLifecycle=true, the normal progression is:
chargeback.createdchargeback.under_reviewchargeback.wonorchargeback.lost
Default timing:
createdimmediatelyunder_reviewafter about 2 minutes- final state after about 20 minutes
When simulateLifecycle=false, only the initial created event is emitted.
Option 1. Trigger It with Raw HTTP
Stripe-shaped simulation
curl -X POST https://cloud.syllecta.example/v1/simulate/chargeback \ -H "Authorization: Bearer ck_acme_staging_demo" \ -H "Content-Type: application/json" \ -d '{ "provider": "stripe", "amount": 2500, "currency": "usd", "transactionId": "ch_demo_123", "simulateLifecycle": true, "finalStatus": "won" }'
Braintree-shaped simulation
curl -X POST https://cloud.syllecta.example/v1/simulate/chargeback \ -H "Authorization: Bearer ck_acme_staging_demo" \ -H "Content-Type: application/json" \ -d '{ "provider": "braintree", "amount": 2599, "currency": "usd", "transactionId": "bt_txn_demo_456", "simulateLifecycle": true, "finalStatus": "lost" }'
Typical success response:
{ "ok": true, "simulation": { "id": "01164cdb-b0a3-469f-b2f5-aad33eb00da7", "status": "created" }, "simulateLifecycle": true, "finalStatus": "won" }
Option 2. Trigger It Through the SDK
Use the normal tenant API key and the simulations resource.
import { BearerAuth, CloudSDK } from "@syllecta/sdk-js"; const sdk = new CloudSDK({ baseUrl: "https://cloud.syllecta.example", auth: BearerAuth(process.env.SYLLECTA_API_KEY!) }); const result = await sdk.simulations.createChargeback({ provider: "stripe", amount: 2500, currency: "usd", transactionId: "ch_demo_123", simulateLifecycle: true, finalStatus: "won" }); console.log(result.simulation.id, result.simulation.status);
Use the SDK path when:
- your test harness already lives in TypeScript,
- you want repeatable QA scripts,
- you want the same auth/client abstraction as the rest of your Syllecta calls.
Minimal Test Callback Example
For a first integration test, use a very small callback receiver so you can confirm payload shape before wiring real business logic.
Example with Express:
import express from "express"; const app = express(); app.use(express.json()); app.post("/webhooks/syllecta", (req, res) => { console.log("received normalized event", { id: req.body.id, provider: req.body.provider, type: req.body.type, transactionId: req.body.payload?.transaction_id ?? req.body.payload?.data?.transaction_id }); res.status(200).json({ ok: true }); }); app.listen(5055, () => { console.log("test callback listening on http://localhost:5055/webhooks/syllecta"); });
Use that as your tenant callback URL:
http://localhost:5055/webhooks/syllectaIf you are testing from a local machine and Syllecta cannot reach localhost directly, expose it through your preferred tunnel and use the public tunnel URL in Backoffice.
Recommended First End-to-End Test
Use this order:
- Set the callback URL in Backoffice.
- Confirm tenant webhook settings are saved.
- Start the test callback receiver.
- Trigger one simulation with
simulateLifecycle=false. - Confirm the callback receives the
createdevent. - Trigger a second simulation with
simulateLifecycle=true. - Wait for
under_reviewand final state events.
This is the safest progression because:
- the first run validates connectivity and payload shape,
- the second run validates lifecycle scheduling and repeated delivery behavior.
What You Should See in Backoffice
After triggering a simulation, open Backoffice and check two places.
1. Webhooks -> Simulations
You should see:
- the simulation row,
- provider,
- amount and currency,
- current simulation status,
- lifecycle control state.
Use this tab to answer:
- was the simulation created,
- which step is currently active,
- is the lifecycle still running, paused, cancelled, or completed.
2. Webhooks -> Events
You should see the emitted webhook events tied to the simulation:
chargeback.createdchargeback.under_reviewchargeback.wonorchargeback.lost
Use this tab to answer:
- was the event logged,
- was it forwarded successfully,
- did the callback fail or retry,
- what normalized payload was actually sent.
Short rule:
- Simulations tab = simulation-level state
- Events tab = per-webhook delivery state
How to Read the Results
If everything is healthy:
- the simulation row advances through expected statuses,
- the event rows reach
processed, - your callback receives the normalized payloads.
If the simulation exists but no event reaches your system:
- check callback URL,
- inspect event delivery status in Backoffice,
- inspect callback logs.
If the first event works but later lifecycle steps do not:
- check whether the simulation was paused or cancelled,
- check lifecycle timing expectations,
- inspect event rows for failed deliveries or retries.
Billing Note
Simulation events go through the webhook delivery pipeline, so they behave like real webhook traffic from an execution perspective.
Important billing behavior:
- simulation delivery is billed only after successful callback processing,
- if webhook billing is disabled for the tenant, simulation traffic does not increment billable usage,
- a full lifecycle run can produce up to three delivered events.
Good QA Patterns
Start with deterministic samples
Set transactionId explicitly so it is easy to trace in logs and downstream systems.
Separate staging from production
Do not mix simulation flows into production callbacks unless that is an intentional drill.
Use one simple callback before real business logic
Validate transport and payload shape first. Then wire the same route into domain handlers.
Test both one-shot and lifecycle mode
You need both:
simulateLifecycle=falsefor simple callback smoke tests,simulateLifecycle=truefor state progression and retry behavior.
Common Mistakes
Expecting the Simulations tab to replace the Events tab
It does not. You need both views.
Forgetting to configure the callback URL first
The simulation can still be created, but you will not validate useful downstream behavior.
Running only full lifecycle tests
Start with one-step smoke tests first. They fail faster and are easier to debug.
Using production-facing business handlers immediately
Use a test callback first if you are still validating payload structure.
Troubleshooting
If the simulation request itself fails:
- check API key,
- check tenant permissions,
- check payload validation.
If the simulation request succeeds but callback delivery fails:
- inspect
Webhooks -> Events, - check callback URL reachability,
- confirm the callback returns
2xx.
If lifecycle steps do not arrive:
- verify
simulateLifecycle=true, - wait for the configured lifecycle windows,
- inspect event history and simulation control state in Backoffice.
Related Docs
This is the workflow guide. Use the API reference when you need exact request/response contract details, and use Backoffice when you need to inspect the resulting simulation and webhook deliveries.