> For clean Markdown of any page, append .md to the page URL.
> For a complete documentation index, see https://docs.recurrente.com/llms.txt.
> For AI client integration (Claude Code, Cursor, etc.), connect to the MCP server at https://docs.recurrente.com/_mcp/server.

# First test payment

> Open a Sandbox, create a checkout, and verify your first payment webhook

Test your integration with isolated data and no real money movement. Use a key from
a named Sandbox to keep test customers, payments, and webhooks separate from production.

## Open your Sandbox

1. [Sign in or create an account](https://app.recurrente.com).
2. Under **Configuración → Llaves API**, select **Abrir Sandbox**. You can also find it in account navigation.
3. During signup, choose **Solo quiero integrar la API** to test before completing live account activation.
4. In **Pruebas (Sandbox)**, choose **Crear mi llave TEST**, name the key, and save the full value. It is displayed once.

You need account administrator access or a Sandbox invitation. The owner can select
**Equipo → Añadir** to email you access to that environment without
granting access to the live account. Open the email link, then confirm with
**Abrir mi Sandbox** on the sign-in page.

Use **Administrar ambientes** to rename, create, or archive Sandboxes before live
activation. You can have up to five active environments; archive one if you need space.

Save the key in your server environment as `RECURRENTE_SECRET_KEY`. Keep it out of
browser code and source control. Creating another key keeps existing keys working;
**Rotar** immediately revokes the old key. No publishable key is required.

## Confirm the environment

```bash
curl https://app.recurrente.com/api/test \
  -H "X-SECRET-KEY: $RECURRENTE_SECRET_KEY"
```

```json
{
  "message": "Hello My business — Pruebas 🌎",
  "account_id": "ac_example",
  "environment": "sandbox",
  "sandbox_id": "sbx_example"
}
```

Expect `environment: sandbox`. `live` identifies production. Save the
`sandbox_id` for webhook verification.

The base URL remains `https://app.recurrente.com/api`. **Your API key selects the
environment. Switching Dashboard screens does not change your integration code.**

## Create a checkout

You do not need to create a product first:

```bash
curl https://app.recurrente.com/api/checkouts \
  -H "X-SECRET-KEY: $RECURRENTE_SECRET_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "items": [{
      "name": "My first test payment",
      "amount_in_cents": 500,
      "currency": "GTQ",
      "quantity": 1
    }]
  }'
```

Save the returned `id` and open `checkout_url`. Amounts are in cents: `500` means
Q5. The minimum is Q5 for GTQ and \$1 for USD.

## Pay and verify

| Outcome | Card                  |
| ------- | --------------------- |
| Success | `4242 4242 4242 4242` |
| Decline | `4000 0000 0000 0002` |

Use a future expiry date, a three-digit CVC, and fictional customer details. Do not
use real cards. Retrieve the checkout using its returned ID:

```bash
curl "https://app.recurrente.com/api/checkouts/$CHECKOUT_ID" \
  -H "X-SECRET-KEY: $RECURRENTE_SECRET_KEY"
```

After success, `status` is `paid`. The payment also appears in **Actividad** in the
same Sandbox. A browser redirect to `success_url` is not proof of payment; verify
the payment on your server. Create a new checkout for each new case.

## Receive the webhook

Expose local port 4242 through a public HTTPS tunnel if developing locally. Register
that public URL, not localhost or a private IP:

```bash
curl https://app.recurrente.com/api/webhook_endpoints \
  -H "X-SECRET-KEY: $RECURRENTE_SECRET_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "url": "https://your-domain.com/webhooks/recurrente",
    "description": "My Sandbox integration"
  }'
```

Save the returned `signingSecret` as `RECURRENTE_WEBHOOK_SECRET`, and the earlier
`sandbox_id` as `RECURRENTE_SANDBOX_ID`. The signing secret differs from your API
key and is returned when you create the endpoint. If you lose it, view the endpoint's
secret in the webhook dashboard.

Download the example server

, then run
these commands in the folder containing the file:

```bash
npm install svix@2.5.0
node webhook-server.mjs
```

It listens at `127.0.0.1:4242/webhooks/recurrente`, verifies Svix signatures against
the raw body, checks the Sandbox, logs event IDs/types, and responds `200`.
Pay **another checkout after registering the endpoint**. Expect
`intent.succeeded` with the expected `sandbox_id`. Its `id` is the unified `in_…` identifier.

Open **Pruebas (Sandbox) → Ver entregas y reintentar** to inspect payloads, your
server's HTTP response, and retries. An event created or accepted by Svix does not
prove your server received it; check the delivery result.

To simulate a delivery failure, restart the example with:

```bash
FAIL_FIRST_DELIVERY=true node webhook-server.mjs
```

The first valid delivery returns `503`. Use **Replay** in the webhook dashboard;
the next attempt returns `200`. Further replays do not process the same `svix-id`
again. The example deduplicates in memory for one local session. For production,
use a durable inbox and queue, with a unique event constraint, before acknowledging
receipt. Accounts predating the change also keep the legacy `payment_intent.succeeded`
event: choose one contract so the same payment is not fulfilled twice.

See [Webhooks](/guides-english/getting-started/webhooks) for signatures and event formats.

## Additional scenarios

| Scenario                                    | How to test                                                                                                                                     |
| ------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------- |
| Decline                                     | Create another checkout and use the decline card; expect `intent.failed`.                                                                       |
| Refund                                      | Send the successful payment intent's actual ID to `POST /refunds`; verify `refund.create`.                                                      |
| Subscription renewal and recovery           | Follow the [Test Clocks guide](/guides-english/guides/sandboxes-and-test-clocks).                                                               |
| Webhook signatures, routing, and duplicates | Use `/test_helpers/webhook_events` or the dashboard's Testing tab. A fixture does not create the corresponding payment, dispute, or withdrawal. |

Download the first-payment Postman collection

.
Set `sandboxKey` locally. The collection captures response IDs and URLs; complete
the payment in the hosted checkout.

Sandbox supports the simulated card outcomes above, subscriptions, and refunds.
It does not reproduce 3DS, bank authorizations, real settlement, or POS hardware.
Transfers, splits, swaps, and withdrawals are blocked. Check each API endpoint's
contract for other resources: an available endpoint does not imply every payment
method or scenario is simulated.

## Troubleshooting

| Symptom                       | Next step                                                                                                                                       |
| ----------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------- |
| Cannot open Sandbox           | Ask your account administrator to open it or invite you to that Sandbox.                                                                        |
| Masked or incomplete key      | Copy the complete key when creating it. Create a new key if lost; rotate the old one only when you intend to revoke it.                         |
| The TEST key "does not exist" | It was your main account's TEST key, which was retired. Create one in the Sandbox. See [retired TEST keys](/guides-english/guides/legacy-test). |
| An object returns 404         | Check the environment and use IDs belonging to that account. Never reuse live IDs.                                                              |
| `sandbox_unsupported`         | The operation is blocked; check its documented limitations.                                                                                     |
| No webhook                    | Check the public URL, environment, signature, and delivery result. Newly registered endpoints do not automatically receive earlier payments.    |
| Webhook dashboard unavailable | Retry from the setup page. Your Sandbox and its data remain available.                                                                          |

## Go live

1. Complete your merchant account's live activation.
2. Configure your server's production environment with a LIVE key.
3. Create the required live products/prices and use their new IDs.
4. Register the LIVE webhook endpoint and its own signing secret.
5. Create new checkouts with the LIVE key.

Test customers, payments, and balances are not promoted to production. Returning to
production in the Dashboard only changes the screen.