Place a real, fulfilled order against the Avrix sandbox and receive a key. Every command below is complete and can be pasted into a terminal.
Time: about 15 minutes. You will end with: a delivered game key, a verified webhook, and a reconciled order record.
Before you start#
You need three things. If you have all three, skip to step 1.
| Requirement | How to get it |
|---|---|
| An Avrix Console account with a seller company | Your Avrix onboarding contact creates this during account setup |
| A sandbox API key | Console → Settings → Integrations → API Keys → Create key, Environment Sandbox, scopes seller:products:read, seller:keys:pull, seller:webhooks:write |
A terminal with curl and jq | jq is optional but the examples use it for readability |
The sandbox catalog is a shared fixture catalog. You do not need a vendor agreement, an allocation, or a territory price list to complete this quickstart — the fixtures already have them. See Authentication for the full credential model.
Set up your shell:
export AVRIX_BASE_URL="https://api.avrix.io"
export AVRIX_API_KEY="avrix_sk_your_key_here"
Paste the key exactly as the console displayed it. Do not add a Bearer prefix, quotes, or any
character the console did not show — the most common first-call failure is a stray character in the
Authorization header.
Step 1 — Confirm connectivity#
curl -s "$AVRIX_BASE_URL/api/seller/v1/health"
{ "status": "ok" }
This endpoint needs no authentication. If it fails, the problem is network or DNS, not credentials.
Step 2 — Confirm your credential#
curl -s -X GET "https://api.avrix.io/api/seller/v1/whoami" \
-H "Authorization: Bearer $AVRIX_API_KEY"{
"data": {
"company": {
"id": "acme-games",
"name": "Acme Games"
},
"sandbox": true,
"hasActiveBma": true,
"key": {
"id": "key_01EXAMPLE",
"name": "Store backend",
"environment": "sandbox",
"scopes": [
"seller:finance:read",
"seller:keys:pull",
"seller:keys:write",
"seller:orders:fulfill",
"seller:orders:manage",
"seller:orders:preview",
"…"
],
"rateLimited": true,
"allowedIps": []
},
"capabilities": {
"preorderEnabled": true,
"backorderEnabled": false,
"checkoutHoldEnabled": true,
"priceCommitmentConfigured": true,
"territoryEnforcementEnabled": true,
"consumerGeoEnforcementEnabled": false
},
"integrationReadiness": {
"sellableSkuCount": 19,
"sharedSkuCount": 19,
"allocatedSkuCount": 19,
"allocatedWithoutShareCount": 0,
"sharedWithoutAllocationCount": 0,
"missingTerritoryPricingCount": 0,
"blockers": [],
"settlementHint": "invoice_report_only",
"settlementMode": "automated_invoice",
"diagnostics": false
},
"warnings": []
}
}
Check three things before continuing. environment must be sandbox. scopes must contain
seller:products:read and seller:keys:pull. capabilities tells you which optional flows are
available on your account — you will not use them in this quickstart, but every guide that
describes an optional capability tells you which field to check.
A 401 here means the key is wrong, revoked, or from the other environment. A 403 means the key
has no scopes. Both are covered in Authentication.
Step 3 — Find something to sell#
The sandbox provides deterministic magic SKUs. Use SANDBOX-ALWAYS-001, which has a continuously
replenished key pool and will not run dry while you are testing.
curl -s "$AVRIX_BASE_URL/api/seller/v1/allocations?sellableOnly=true&limit=5" \
-H "Authorization: Bearer $AVRIX_API_KEY" | jq '.data[] | {skuCode, sellable, pricePerKeyCents, remaining}'
{
"skuCode": "SANDBOX-ALWAYS-001",
"sellable": true,
"pricePerKeyCents": 5999,
"remaining": 99412
}
Step 4 — Preview before you charge#
Preview tells you whether the order can be fulfilled right now and what price Avrix will hold you to. Call it when the buyer reaches checkout, not on every page view.
Every API-model preview and order carries an integrationOrderContext with the gross price you
charge the buyer (salesPriceGrossMinor), the tax rate you applied (salesTaxRatePercent, 0
where no tax applied) and the channel the sale came through (salesChannel). These are required
in Sandbox and in Production alike; omitting them is refused with TAX_DECLARATION_REQUIRED or
SALES_CHANNEL_REQUIRED before any key moves. Production keys additionally declare the buyer's
IP as consumerIp (top level or inside the context); see
Integration order context.
curl -s -X POST "$AVRIX_BASE_URL/api/seller/v1/orders/preview" \
-H "Authorization: Bearer $AVRIX_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"skuCode": "SANDBOX-ALWAYS-001",
"quantity": 1,
"countryCode": "US",
"integrationOrderContext": {
"schemaVersion": 1,
"salesCountryCode": "US",
"currencyCode": "USD",
"salesPriceGrossMinor": 7999,
"salesTaxRatePercent": 0,
"salesChannel": "web"
}
}' | jq
{
"data": {
"type": "order_preview",
"fulfillmentType": "fulfilled",
"allocationId": "07ea0905-152b-8f0d-8261-78eb28f91f27",
"allocationRemaining": 999768,
"inventoryAvailable": 100,
"maxFulfillableQuantity": 100,
"canFulfill": true,
"partialFulfillmentExpected": false,
"cannotFullyFulfill": false,
"allOrNothingWillFail": false,
"lines": [
{
"skuId": "07ea0816-162b-8822-8385-1d0609d86dd3",
"skuCode": "SANDBOX-ALWAYS-001",
"quantity": 1,
"unitPriceCents": 5999,
"lineTotalCents": 5999,
"currencyCode": "USD",
"currencyExponent": 2,
"initialUnitPriceCents": 5999,
"promo": null,
"promoResolution": "none",
"activation": {
"type": "worldwide",
"countries": null
}
}
],
"totalCents": 5999,
"currencyCode": "USD",
"currencyExponent": 2,
"priceCommitment": {
"token": "pct1.<token>",
"expiresAt": "2026-09-06T13:31:39.000Z"
},
"priceCommitmentToken": "pct1.<token>",
"estimateOnly": true
}
}
Two fields decide what happens next.
canFulfill must be true before you charge the buyer. If it is false, Avrix cannot deliver
the requested quantity and POST /orders will fail — Avrix never partially fulfils. Disable the
pay button or reduce the quantity.
unitPriceCents is the price you must echo back on the order as expectedUnitPriceCents. This
is how Avrix guarantees you are never charged a price you did not see. If the price moves between
preview and order, the order is rejected rather than silently repriced.
Step 5 — Charge the buyer#
This step happens entirely in your own system, at your own payment processor. Avrix is not involved and has no visibility into it.
Capture the payment reference your processor returns — you will send it to Avrix as commercial evidence. In sandbox a placeholder is accepted; in production a real reference is required.
Step 6 — Create the order#
curl -s -X POST "$AVRIX_BASE_URL/api/seller/v1/orders" \
-H "Authorization: Bearer $AVRIX_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: store-order-10432" \
-d '{
"skuCode": "SANDBOX-ALWAYS-001",
"quantity": 1,
"orderReference": "store-order-10432",
"expectedUnitPriceCents": 5999,
"countryCode": "US",
"integrationOrderContext": {
"schemaVersion": 1,
"salesCountryCode": "US",
"currencyCode": "USD",
"salesPriceGrossMinor": 7999,
"salesTaxRatePercent": 0,
"salesChannel": "web",
"paymentProcessorReference": "sandbox_pay_10432",
"checkoutSessionId": "cs_sandbox_10432"
}
}' | jq
{
"data": {
"type": "fulfilled",
"skuCode": "SANDBOX-ALWAYS-001",
"orderReference": "store-order-10432",
"pulled": 1,
"keys": ["SBOX-ATZQ4-9WFKD-2M7XR"],
"keyIds": ["8f21a9b0-1234-5678-9abc-def012345678"]
}
}
Three parts of that request deserve attention.
Idempotency-Key makes the request safe to retry. Send the same value with the same body and
you get the original result back rather than a second fulfilment. Use a value that is stable for
the logical order — your checkout session id is ideal.
Integration recipes covers the full contract.
orderReference is your business identifier. It is immutable, it appears on finance records,
and it is how you look the order up later. It is not a substitute for the idempotency key; the two
do different jobs.
integrationOrderContext declares the retail transaction: what the buyer paid, where they
were, and which payment it corresponds to. Sandbox accepts a minimal set. Production enforces a
fuller set including tax and payment-processor references — see Sandbox to
production.
Step 7 — Verify the order#
curl -s "$AVRIX_BASE_URL/api/seller/v1/orders?orderReference=store-order-10432" \
-H "Authorization: Bearer $AVRIX_API_KEY" | jq
This is also your recovery path. If step 6 ever times out and you do not know whether it succeeded, do not re-charge and do not issue a new idempotency key. Either replay the identical request with the identical key, or look the order up by reference. See Order lifecycle.
Step 8 — Receive a webhook#
Register an endpoint to receive asynchronous outcomes. Use any HTTPS URL you control; a request inspector works for this test.
curl -s -X POST "$AVRIX_BASE_URL/api/seller/v1/webhooks" \
-H "Authorization: Bearer $AVRIX_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: webhook-setup-1" \
-d '{
"url": "https://your-endpoint.example.com/avrix/webhooks",
"events": ["order.fulfilled", "order.failed"]
}' | jq
{
"data": {
"id": "wh_7d3f...",
"url": "https://your-endpoint.example.com/avrix/webhooks",
"events": ["order.fulfilled", "order.failed"],
"secret": "whsec_2fA...",
"status": "active"
}
}
Send a test delivery:
curl -s -X POST "$AVRIX_BASE_URL/api/seller/v1/webhooks/wh_7d3f.../test" \
-H "Authorization: Bearer $AVRIX_API_KEY" | jq
Your endpoint receives a test.ping with an X-Avrix-Signature header. Verify that signature
before trusting any webhook body — the verification code and the full delivery contract are in
the webhook guide.
You are done#
You have completed the full transaction cycle: catalog, preview, order, fulfilment, and events.
What to build next#
| Next | Why |
|---|---|
| Catalog and allocations | Your real catalog is not one call — get the sync model right before writing it |
| Order lifecycle | Every failure mode, including price drift, sold-out inventory, and unknown outcomes |
| Webhook guide | Signature verification, retries, deduplication |
| Error reference | Every code, when it occurs, and whether retrying is safe |
| Sandbox to production | What changes when you go live, and what certification requires |
Try a failure path#
The sandbox includes deterministic failure fixtures. Running these now is cheaper than discovering them in production.
| SKU | Produces |
|---|---|
SANDBOX-NOKEYS-001 | 409 NO_AVAILABLE_KEYS — the post-payment refund path |
SANDBOX-PRICE-MIS-001 | Price mismatch when expectedUnitPriceCents is stale |
SANDBOX-NOALLOC-001 | Shared but unallocated — sellable: false |
SANDBOX-LIFECYCLE-001 | Product not live — PRODUCT_NOT_SELLABLE |
SANDBOX-TERR-BLOCK-001 | Territory rejection — price list is DE-only, so send US |