# Testing Your Integration

ShipWave has no sandbox and no test keys. Every API key works against your live account. You can still build and check most of an integration without spending money:

| Step | Cost | Notes |
|------|------|-------|
| List and read orders, shipments, products, warehouses, stores | Free | Read-only. |
| Create, update, and cancel orders | Free | Orders are created in your ShipWave account only. Creating an order never buys a label. Use an `orderNumber` such as `TEST-1001` so test orders are easy to spot. `DELETE /orders/:id` cancels the order in ShipWave. |
| Get rates (`POST /orders/:id/rates`) | Free | Returns live carrier rates and a purchase token. Nothing is bought until you call `/labels`. |
| Subscribe to webhooks | Free | Use a request-inspection URL you control. Creating and cancelling orders through the API sends `order.created` and `order.updated`. |
| Verify addresses | Free allowance, then billed | See [Address Verification](addresses.md) for the allowance and pricing. |
| Purchase a label (`POST /orders/:id/labels`) | **Charged** | This is a real label. |

## Testing a Label Purchase

To test the full purchase flow:

1. Create an order shipping to a real, deliverable address you control.
2. Get rates and buy the recommended rate.
3. Void it straight away with `POST /shipments/:id/void`.

Labels can only be voided before the carrier scans them. Refund timing depends on the carrier and the payment method; see [Shipments](shipments.md).

## Before You Go Live

- Store your key server-side and plan its rotation. Keys expire one year after they are created; see [Authentication](authentication.md).
- Handle `429` responses using `Retry-After`; see [Rate Limiting](rate-limiting.md).
- Make order creation idempotent with `externalId` or an `Idempotency-Key` header, so a retried request returns the original order instead of creating a duplicate.
- Treat webhook deliveries as at-least-once and verify every signature; see [Webhooks](webhooks.md).
