qyr_test_ vs qyr_live_ distinction, how to verify events landed, and how to debug the most common errors.
Two key prefixes
Every API key Qyren issues starts with one of two prefixes:
Both keys are tied to the same
game_id, so the schema and request shapes are identical. You can switch between them by changing only the Authorization header.
Getting a test key
In the dashboard, open Settings → API Keys. There’s a “Generate test key” button alongside the live key controls. Test keys are free, you can have multiple, and you can revoke and regenerate at will. Keep test and live keys separate in your environment config. A common pattern:Verifying events landed
To confirm events made it to Qyren, open the dashboard at https://app.qyren.ai and go to the Events view for your game. It shows the most recent events received, including event type, player ID, and arrival time. Use it during integration to confirm your batching, ULID generation, and field formatting are correct. Events typically appear within a few seconds of acceptance.A reasonable test plan
- Send one event. Pick
session_start— minimal required fields. Confirm202 Accepted. - Send a batch with one bad event. Make one event missing a required field (e.g., a
purchasewithoutrevenue_usd). Confirm207, inspect theerrors[]array, confirm only the bad event was rejected. - Import 2–3 offers. Confirm
200 OKwith the rightimportedcount. - Request a recommendation. Confirm you get an ordered offer list back.
- Send
offer_impressionevents for each returned offer. - Send a
purchaseevent for one of them. - Open the Events view in the dashboard. Confirm everything you sent shows up in the order you expect.
qyr_test_ key, swap in your qyr_live_ key and you’re ready for production traffic.
Common 400 errors
These are the failures you’ll hit most often during integration. Every one of them surfaces in theerrors[] array of a 207 response (or the details of a 400).
event_id: must be a valid ULID
Your event_id isn’t a 26-character Crockford Base32 string. UUIDs and incrementing integers are not accepted. Use a ULID library — every modern language has one. Optionally prefix with evt_ (the ULID is parsed from after the last underscore).
event_time: more than 7 days in the past / more than 7 days in the future
Server clock skew, or you’re replaying very old logs. Qyren rejects anything outside ±7 days of its server clock to keep analytics windows clean. Sync your server time (NTP), and if you really need to backfill ancient data, email [email protected] for a one-shot import.
properties.<field>: required for <event_type> events
You’re missing one of the per-type required properties. Check Event Schema for the exact list. The most common offenders:
purchasemissingis_first_purchase(it’s a boolean — sendfalse, not omit it).currency_earned/currency_spentmissingbalance_after.offer_impressionmissingposition.
player_id: must be 1-64 chars, alphanumeric/underscore/hyphen only
Spaces, dots, colons, or @ in your player_id. Hash or remap to a clean opaque ID.
geo.country: must be a valid ISO 3166-1 alpha-2 code
You sent usa, united states, or US (trailing space). It must be exactly two uppercase letters: US, DE, JP.
Debugging the 207 response
Whenever you see207 Multi-Status, walk the errors[] array. Each entry has:
event_index— position in the array you sent. Match it back to your local buffer.event_id— the failing event’s ID, if it had one.field— which field failed.error— human-readable reason.
When things still don’t work
Email [email protected] with:- Your
game_id. - A sample of the request body that failed (redact the
Authorizationheader). - The full response body.

