Wallet Identity Lookup
Fetch every email attached to a player's Invo wallet so your UI can pre-populate the "your other accounts" list instead of asking the user to type each email manually.
When to use this endpoint
One human can legitimately operate multiple emails under one verified phone (parent + child, separate gaming handles, family-shared device). Once a phone is verified to a wallet, additional emails can be attached via the phone-share OTP flow. This endpoint returns the full email list for a player's wallet so your client can show the user "you also have x@y.com on this account" without making them remember.
Typical UX: after a player logs in, call this endpoint with their email or phone to fetch the rest of their wallet's emails, then render them as switchable account chips in your UI.
Endpoint
GET /api/wallet/identities?player_email=<email> GET /api/wallet/identities?player_phone=<E.164 phone> Headers: X-Game-Secret-Key: <your_game_secret_key>
Provide either player_email or player_phone (or both — they'll be AND-filtered). The player must already exist in your game; the endpoint does not return data for players in other games on the network.
Authentication
Game secret only. No JWT required.
The endpoint requires X-Game-Secret-Key in the header. Game secrets are server-side credentials — they should never appear in a browser, mobile app binary, or any client-shipped code. Call this endpoint from your game's backend, not directly from a player's device.
The response is automatically scoped to your game — you cannot use this endpoint to enumerate wallet emails for players that have no Player row in your game. Cross-game lookups return 404, intentionally indistinguishable from "player doesn't exist anywhere."
Response shape
Success (200) — wallet-bound player
{
"status": "success",
"player_email": "alice@example.com",
"player_phone": "+15551234567",
"wallet_user_id": "9f3e2d1c-4b5a-6c7d-8e9f-0a1b2c3d4e5f",
"primary_phone": "+15551234567",
"primary_email": "alice@example.com",
"is_minor": false,
"emails": [
{ "email": "alice@example.com", "primary": true, "verified_at": "2026-04-12T19:21:00+00:00" },
{ "email": "alice.work@example.com", "primary": false, "verified_at": "2026-04-15T11:08:00+00:00" },
{ "email": "alice.gaming@example.com","primary": false, "verified_at": null }
]
}Success (200) — legacy player without wallet
Players created before the wallet rollout (2026-05-04) may not have a wallet binding yet. The endpoint gracefully degrades to a single-email payload:
{
"status": "success",
"player_email": "legacy@example.com",
"player_phone": "+15551234567",
"wallet_user_id": null,
"primary_phone": "+15551234567",
"primary_email": "legacy@example.com",
"is_minor": false,
"emails": [
{ "email": "legacy@example.com", "primary": true, "verified_at": null }
],
"message": "Player has no wallet binding."
}Errors
| Status | When | Body |
|---|---|---|
| 401 | X-Game-Secret-Key missing or unknown | { "status": "error", "message": "Invalid game secret." } |
| 400 | Neither player_email nor player_phone provided | Descriptive 400 error |
| 400 | Phone provided but fails E.164 validation | "Invalid 'player_phone' format..." |
| 404 | Player not in your game (intentionally same as "not on network") | "Player not found in this game." |
| 429 | Rate-limited (60 requests/minute per IP) | Standard rate-limit response |
Privacy and scope
What you cannot do with this endpoint
- Look up wallets for phones / emails that have never been in your game
- Enumerate across the network using a sequence of phone numbers
- Distinguish between "player exists in another game" and "player doesn't exist" — both return
404 - See the guardian relationship for a minor account beyond the
is_minorflag —guardian_wallet_user_idis intentionally not exposed
The 404 responses are uniform on purpose. A site-wide list of every phone+email-wallet pair would be a phishing primitive; refusing to confirm or deny existence outside your own game prevents that.
Implementation tips
- Cache aggressively per session. The wallet email list changes infrequently — fetch once after login and reuse for the player's session unless they explicitly add or remove an email.
- Render the
primary: trueemail first. Treat it as the canonical user identity in your UI. - Treat
verified_at: nullas pending. An email with no verified timestamp was added via phone-share OTP but never finished the second-factor confirmation — visually distinguish it from a fully verified email. - Don't store the response client-side beyond the session. Wallet email lists are sensitive — keep them in memory or short-lived storage, never
localStoragelong-term. - Sort client-side if you care about order. Response order is currently DB-insertion order and is not part of the API contract.
Quick start
# Look up by email
curl -H "X-Game-Secret-Key: your_game_secret_key_here" \
"https://invo.network/api/wallet/identities?player_email=alice@example.com"
# Look up by phone (E.164)
curl -H "X-Game-Secret-Key: your_game_secret_key_here" \
"https://invo.network/api/wallet/identities?player_phone=%2B15551234567"