Avrix enforces regional SKU territory and declared sales country on production API-key orders before keys are pulled. Buyer IP geo / VPN verification defaults to enforce on production keys. Sandbox keys stay off. Discover the live posture via GET /whoami:
| Phase | Capability bits | Behavior |
|---|---|---|
| Off | both false | IP shape only (CONSUMER_IP_INVALID for reserved/private addresses). Sandbox keys are always this phase. |
| Monitor | consumerGeoMonitorEnabled | Full evaluation; request succeeds with additive geoWarning |
| Enforce | consumerGeoEnforcementEnabled | Default for production keys when geo data is loaded. Mismatches and high-risk IPs return 422; geo data outage returns 503 |
Reserve and hold now run the same territory and geo gates as preview (API-model allocations) so a region-mismatched buyer fails before payment.
Whatever the phase, the country the gate resolved from the buyer IP is persisted next to the
declared salesCountryCode on every API-model sale and pull row: GET /finance/api-sales,
GET /finance/statements lines and GET /transactions return detectedCountryCode,
countryMismatch and warnings: ["COUNTRY_MISMATCH"], and the api-sales CSV carries
customerRegionCode, detectedCountryCode, warning columns. In monitor mode this is the
fraud signal for a sale that went through with a geoWarning; the raw IP is never returned.
Example#
curl -s -X GET "https://api.avrix.io/api/seller/v1/availability?skuCode=SANDBOX-ALWAYS-001&quantity=1&country=US" \
-H "Authorization: Bearer $AVRIX_API_KEY"Market region locks as VPN-proof only when capabilities.consumerGeoEnforcementEnabled
is true. Territory allowlists and catalog list-price checks are always enforced on
production keys. Sandbox keys never enable IP-country or VPN/hosting checks.
Defense layers#
- SKU territory (always) —
salesCountryCode/ previewcountryCodemust be in the publisher region country pack for that SKU (minus release exclusions). - Catalog list price (always) —
expectedUnitPriceCentsmust match POST /orders/preview for the same country. - Buyer IP shape (when geo enforcement is on) — production keys require
integrationOrderContext.consumerIp(public routable IPv4/6); private/reserved IPs are always rejected when supplied. - Geo cross-check (when geo enforcement is on) — IP-derived country must equal
salesCountryCode. - VPN / hosting block (when geo enforcement is on and anonymous-IP data is loaded) — VPN, hosting, Tor, and public proxy IPs are rejected.
- Restricted jurisdiction (always) — some locations are not available for access or sale (LOCATION_BLOCKED on your API source IP; COUNTRY_RESTRICTED on the sales country or buyer IP).
- Checkout-page confirmation, standard on production connections (
GET /allocations.consumerGeoAttestRequired, alsowhoami.capabilities.consumerGeoAttestRequired). CallPOST /geo/attest, loadpixelUrlin the buyer browser, and sendconsumerGeoAttestIdon preview/hold/reserve/commit. Completing the pixel from your store server is rejected (CONSUMER_IP_INVALID).
When geo enforcement is off, a supplied malformed IP is still rejected. Sandbox / non-production keys never enable IP-country or VPN/hosting checks, and never require checkout-page confirmation.
Partner integration checklist#
- Discover territory —
GET /products/{id}→releases[].allowedCountries;GET /regions→allowedCountriesper region code. - Availability first —
GET /availabilitywith the buyer's country at product-page time and hide the buy button when the SKU is not sellable there. - Preview —
POST /orders/previewwithcountryCodeand the real buyer IP at cart time. - Reserve / hold — send the same
countryCodeplusintegrationOrderContext.consumerIpbefore charging. Under enforcement, a missing IP returnsCONSUMER_IP_REQUIRED. - Order — include both fields in
integrationOrderContext:
{
"expectedUnitPriceCents": 450000,
"integrationOrderContext": {
"schemaVersion": 1,
"salesCountryCode": "JP",
"consumerIp": "203.0.113.42",
"currencyCode": "JPY",
"salesPriceGrossMinor": 598000
}
}
- Capture the end-buyer public IP — see Which IP to send.
- Re-preview on
CATALOG_PRICE_MISMATCH,TERRITORY_*, orCONSUMER_GEO_MISMATCH. - Discover posture —
GET /whoami→consumerGeoEnforcementEnabled/consumerGeoMonitorEnabled. During monitor, treatgeoWarningin 200 responses as a would-be 422.
Which IP to send#
Send the end-buyer's public IPv4 or IPv6, not your store backend's egress address.
- Read it from your CDN or load-balancer headers after you trust the hop (for example
CF-Connecting-IPon Cloudflare, or the first public address inX-Forwarded-Forbehind nginx). - Do not forward an unverified client-supplied header.
- Sending your server or NAT egress IP will usually trip
CONSUMER_IP_HIGH_RISK(hosting range). Prompt the buyer to disable VPN when you receive that code.
Sandbox keys can exercise the same codes without geo data using
203.0.113.66 (CONSUMER_GEO_MISMATCH) and 203.0.113.99 (CONSUMER_IP_HIGH_RISK).
See Sandbox.
Error codes#
| Code | HTTP | Meaning |
|---|---|---|
TERRITORY_NOT_ALLOWED_FOR_SKU | 422 | Country not in SKU region pack |
TERRITORY_EXCLUDED_FOR_RELEASE | 422 | Country blocked for this release |
SKU_REGION_UNRESOLVED | 422 | SKU has no territorial coverage configured |
FOOTPRINT_COUNTRY_NOT_DECLARED | 422 | Country missing from your selling footprint. See footprint-country-not-declared. |
REGION_LOCKED_FOR_CONTRACT | 422 | Content Owner locked this country under your agreement. See region-locked-for-contract. |
REGION_LOCKED_FOR_SKU | 422 | Content Owner locked this title in that country. See region-locked-for-sku. |
CONSUMER_GEO_MISMATCH_POLICY_REFUSED | 422 | Declared and evidenced countries disagree, and the agreement refuses that. See consumer-geo-mismatch-policy-refused. |
CONSUMER_IP_REQUIRED | 422 | Missing consumerIp (enforce mode; also at reserve/hold) |
CONSUMER_IP_INVALID | 422 | Private/reserved IP (always, including monitor/off) |
CONSUMER_GEO_MISMATCH | 422 | IP country ≠ salesCountryCode. Details include declaredCountry and detectedCountry (never the raw IP). |
CONSUMER_IP_HIGH_RISK | 422 | VPN/hosting/Tor/proxy detected — prompt the buyer to disable VPN |
CONSUMER_GEO_UNAVAILABLE | 503 | Geo data temporarily unavailable. Retry with the same Idempotency-Key after Retry-After. |
COUNTRY_RESTRICTED | 422 | Sales are not available for this location (platform restricted jurisdiction or catalog restricted country). |
CONSUMER_GEO_ATTEST_REQUIRED | 422 | This catalog requires confirming the buyer’s network from the checkout page. Call POST /geo/attest, load pixelUrl in the buyer browser, then retry with consumerGeoAttestId. |
LOCATION_BLOCKED | 403 | Access is not available from the current location (authenticated API source IP). |
Activation territory (where the key redeems)#
Sale territory answers "may I sell here?"; activation territory answers "will the delivered key actually redeem there?" — vendors can stock region-locked keys for a SKU whose sale territory is broader. The API exposes the guaranteed activation set per SKU, computed across all inventory batches (a mixed pool of worldwide and region-locked batches reports the most restrictive intersection, because your pull may be served from any batch):
GET /products/{productId}— each SKU carriesactivation: { type, countries }wheretypeisworldwide,region_locked, orunknown(no unarchived batch metadata) andcountriesis the guaranteed ISO 3166-1 alpha-2 list (null when worldwide or unknown).GET /allocations— every row carries the sameactivationblock.POST /orders/preview—lines[].activation, plus the advisory warningACTIVATION_REGION_MISMATCHwhen yourcountryCodefalls outside the guaranteed set andwhoami.capabilities.activationPolicyiswarn(the default).POST /orders,POST /keys/pull,POST /orders/{orderId}/fulfill— the success body carries the sameactivationblock next to the delivered keys (also on pre-order acceptances), so "activates in" can be shown at delivery time without a second lookup. It is resolved after the pull commits; if inventory metadata cannot be read at that moment the field degrades to{ "type": "unknown", "countries": null }rather than failing a delivered order.
activationPolicy is also on GET /integration/status. Modes:
| Policy | Preview warning | Transact paths (preview, hold, reserve, order, bulk, commit, pull, export) |
|---|---|---|
warn (default) | ACTIVATION_REGION_MISMATCH | Not blocked |
enforce_known | — | 422 ACTIVATION_REGION_MISMATCH for a known locked-set miss |
enforce_strict | — | Also 422 ACTIVATION_REGION_UNKNOWN when coverage is absent |
Missing countryCode is never an activation 422 (sale territory remains a separate gate).
Region catalog#
Discover countries and region packs the platform exposes:
| Method | Path | Purpose |
|---|---|---|
GET | /api/seller/v1/regions | List regions / country packs |
GET | /api/seller/v1/regions/{code} | Detail for one region code |
Use these to validate buyer-country pickers and map storefront locales to ISO country codes before preview. Territory allow/deny for a SKU still comes from preview / commit errors and allocation readiness — the regions catalog is reference data, not a sellability gate by itself.
Limits#
- Declared
salesCountryCoderemains authoritative for territory allowlists even when geo is off. - When anonymous-IP data is unavailable, VPN/hosting blocks are skipped but country cross-check still runs when country geo data is present.
- Sandbox / non-production keys never enable IP-country or VPN/hosting layers. Shared sandbox catalog magic IPs (
203.0.113.66,203.0.113.99) still return the matchingCONSUMER_*codes so integrators can test.