# Testing (/developers/testing)

Fianto has no test mode and no test keys. You test your webhook route with signed sample events from
the CLI, check real delivery with a `test.event`, forward your application's real events to your
own machine, and run payments against a self-hosted or local Fianto backend that runs on devnet or
localnet.

> **One cluster per deployment:**
>
> Each Fianto deployment runs on exactly one Solana cluster: `mainnet-beta`, `devnet` or `localnet`.
> Secrets start with `fian_sk_live_` on every deployment, devnet included, so the prefix does not tell
> you which cluster you are on. The deployment your `FIANTO_BASE_URL` points at does: the cluster is
> that backend's own setting, not something the URL selects.

## Before you start [#before-you-start]

* Node.js 20.3 or later, to run the CLI with `npx @fianto/cli`.
* Your webhook route runs on your machine, for example at
  `http://localhost:3000/api/webhooks/fianto`.
* For the steps that call the API: your app id and secret, exported in your shell as
  `FIANTO_APP_ID` and `FIANTO_APP_SECRET`. A `.env` file is not enough; the CLI reads the shell.
* `FIANTO_WEBHOOK_SECRET` exported too, or passed with `--secret`.

## Steps [#steps]

**Step 1.**

### Send your webhook route a signed sample [#send-your-webhook-route-a-signed-sample]

`fianto trigger` signs a realistic sample of any event type and posts it to your route. It makes no
API call and needs only the webhook secret.

```bash
npx @fianto/cli trigger order.paid \
  --forward-to http://localhost:3000/api/webhooks/fianto \
  --secret "$FIANTO_WEBHOOK_SECRET"
```

`--forward-to` accepts only a local address (`localhost`, `127.0.0.0/8` or `[::1]`) unless you add
`--allow-remote`. Try each event type your route handles, such as `subscription.created` or
`order.expired`.

**Step 2.**

### Check the credentials the CLI uses [#check-the-credentials-the-cli-uses]

```bash
npx @fianto/cli whoami
```

It calls `GET /v1/application` and prints the application and merchant your `FIANTO_APP_ID` and
`FIANTO_APP_SECRET` belong to, and the webhook URL with its status. The remaining steps call the API
with these credentials.

**Step 3.**

### Send a real test.event [#send-a-real-testevent]

Ask Fianto to deliver a signed `test.event` to your application's verified webhook URL. Its `data`
is `{ "message": "This is a test event from fianto." }`. It is the only event you can ask for:
Fianto never sends business events such as `order.paid` on request.

#### CLI

```bash
npx @fianto/cli trigger test.event
```

#### curl

```bash
curl -X POST https://api.fianto.xyz/v1/webhook/test-event \
  --user "$FIANTO_APP_ID:$FIANTO_APP_SECRET" \
  -H "Idempotency-Key: test-event-1"
```

The API answers `202` with `{ "event_id": "evt_…" }`.

You can send 10 test events per hour per application. The dashboard's test button uses the same
budget.

**Step 4.**

### Forward your real events to your machine [#forward-your-real-events-to-your-machine]

`fianto events tail` polls your application's events and posts each new one, re-signed with your
webhook secret, to a URL you give it, such as your local route. It runs until you press Ctrl-C.

```bash
npx @fianto/cli events tail \
  --forward-to http://localhost:3000/api/webhooks/fianto \
  --since 5m
```

Each event is forwarded once per run, with no retry. If your route is down when an event arrives,
that event is not forwarded again in this run. This is a development aid; real deliveries go to your
verified webhook URL.

**Step 5.**

### Run payments against a self-hosted or local backend [#run-payments-against-a-self-hosted-or-local-backend]

To pay and subscribe for real without mainnet funds, use a self-hosted or local Fianto backend
that runs on devnet or localnet, with an application created on that deployment. Point the SDK and
the CLI at it:

```bash
export FIANTO_BASE_URL=http://localhost:3000   # your local fianto backend
```

`FIANTO_BASE_URL` must be `https`, except for `localhost`, `127.0.0.1` and `[::1]`, and must not end
in `/v1`. There is no separate devnet API host to switch to.

## Check it worked [#check-it-worked]

> `fianto trigger order.paid --forward-to …` prints `→ 200 order.paid (local sample)` and your route
> logs the sample order. After `trigger test.event`, your route receives a `test.event`, and it shows
> up in `npx @fianto/cli events list --type test.event`.

## Troubleshooting [#troubleshooting]

| You see | Why | Fix |
|---|---|---|
| The CLI refuses the `--forward-to` URL | `trigger` posts signed samples only to a local address. | Forward to `localhost`, or add `--allow-remote` if you really mean a remote URL. |
| `trigger order.paid` without `--forward-to` exits with code 2 | Only `test.event` can be sent through Fianto. Other types exist only as local samples. | Add `--forward-to`, or trigger `test.event`. |
| 409 `webhook_endpoint_not_active` on `test-event` | The application has no verified webhook URL, so there is nowhere to deliver. | Set the URL in the dashboard and let it pass verification. |
| 429 on `test-event` | You sent 10 test events in the last hour, counting the dashboard button. | Wait and use `trigger --forward-to` meanwhile. |
| 401 `invalid_api_credentials` against a self-hosted or local backend | Your keys belong to another deployment, or `FIANTO_BASE_URL` is not set and the call went to `https://api.fianto.xyz`. | Create an application on the deployment you test against, and set `FIANTO_BASE_URL` to it. |
| Your route answers 400 `invalid_webhook` to samples | The route and the CLI use different webhook secrets. | Pass the same `whsec_…` secret your route verifies with. |

## See also [#see-also]

- [CLI](/developers/cli): Every Fianto command and flag.

- [Verify webhooks](/developers/webhooks/verify): How your route checks the signature.

- [Authentication](/developers/authentication): App ids, secrets and the 401 answer.