Phoenix Gamification
Referrals

Querying Results

This guide explains how to query referral and affiliate data for end-user and admin experiences, including **response shapes** you can map directly to UI.

Which gateway?

AudienceBase URLAuth
Gamers / end usersQuery GatewayGamer JWT (user_type: gamer)
Admin / back officeAdmin GatewayZitadel / admin JWT (scopes wallet:read / wallet:write)

Important: Admin-only routes (create/update/delete campaigns, affiliates, tiers, rules, hierarchy) exist only on the Admin Gateway under /v1/.... Calling them on the Query Gateway returns 404 — that gateway only exposes the gamer routes below.


Overview (gamer — Query Gateway)

Use these to drive in-app referral UI:

  • list a user’s referral codes
  • list referrals created by a user (as referrer)
  • check inbound referral attribution (my-attribution)
  • check affiliate account status
  • list affiliate commissions
  • list affiliate downline

All list endpoints return:

{ "total": <number>, "items": [ ... ] }

total is the number of rows in this page (not a global count across the whole dataset).


Query Referral Codes

List referral codes for the authenticated user.

Endpoint (Query Gateway):

GET /v1/referral/codes?limit={limit}&offset={offset}
Authorization: Bearer <GAMER_JWT>

200 Response (application/json):

{
  "total": 1,
  "items": [
    {
      "id": "417274ca-0e95-4024-a5c8-a3c3eecc6337",
      "tenant_id": "phoenix_test_tenant",
      "user_id": "3b435312-4359-49da-99b5-b60cc45152c4",
      "campaign_id": null,
      "code": "RF422C71DF",
      "usage_limit": 100,
      "usage_count": 0,
      "expires_at": null,
      "active": true,
      "created_at": "2026-03-23T10:42:19.013787Z",
      "updated_at": "2026-03-23T10:42:19.013787Z"
    }
  ]
}

UI ideas: show code, usage_count / usage_limit, expires_at, active.


Query Referrals Created by User

List referrals where the authenticated user is the referrer.

Endpoint (Query Gateway):

GET /v1/referral/referrals?status={status}&limit={limit}&offset={offset}
Authorization: Bearer <GAMER_JWT>

200 Response:

{
  "total": 1,
  "items": [
    {
      "id": "b8b6e954-b4d7-4d67-97a1-0d46f2d2aec7",
      "tenant_id": "phoenix_test_tenant",
      "campaign_id": null,
      "code_id": "da0b1cc8-7697-45e7-a8e1-e60aa0b43fb5",
      "referrer_id": "3b435312-4359-49da-99b5-b60cc45152c4",
      "referee_id": "0a36729d-e460-4e26-9ac9-fce3f1f8c944",
      "status": "pending",
      "qualified_at": null,
      "rewarded_at": null,
      "created_at": "2026-03-23T10:33:00.356349Z",
      "updated_at": "2026-03-23T10:33:00.356349Z"
    }
  ]
}

Statuses: pending, qualified, rewarded, expired, cancelled (see backend validation).

UI ideas: table of referees with status; show qualified_at / rewarded_at when set.


Query My Attribution

Whether the current user was referred (referee side).

Endpoint (Query Gateway):

GET /v1/referral/my-attribution
Authorization: Bearer <GAMER_JWT>

200 Response — not referred:

null

200 Response — referred:

{
  "id": "b8b6e954-b4d7-4d67-97a1-0d46f2d2aec7",
  "tenant_id": "phoenix_test_tenant",
  "campaign_id": null,
  "code_id": "da0b1cc8-7697-45e7-a8e1-e60aa0b43fb5",
  "referrer_id": "3b435312-4359-49da-99b5-b60cc45152c4",
  "referee_id": "0a36729d-e460-4e26-9ac9-fce3f1f8c944",
  "status": "pending",
  "qualified_at": null,
  "rewarded_at": null,
  "created_at": "2026-03-23T10:33:00.356349Z",
  "updated_at": "2026-03-23T10:33:00.356349Z"
}

UI ideas: if null, show “Apply a code”; if object, show inviter id and progress (status).


Query Affiliate Status

Whether the authenticated user has an affiliate row (Query Gateway — gamer).

Endpoint:

GET /v1/affiliate/status
Authorization: Bearer <GAMER_JWT>

200 Response — not an affiliate: typically 400 with an error body (product may treat as “no affiliate”). When the user is an affiliate:

{
  "id": "719665df-2932-4f87-92a9-aa8174d139aa",
  "tenant_id": "phoenix_test_tenant",
  "user_id": "3b435312-4359-49da-99b5-b60cc45152c4",
  "affiliate_code": "AFFFE914C5B",
  "status": "active",
  "tier_id": null,
  "parent_affiliate_id": null,
  "hierarchy_path": null,
  "hierarchy_depth": 0,
  "approved_at": "2026-03-23T10:42:25.572608Z",
  "suspended_at": null,
  "created_at": "2026-03-23T10:42:25.596880Z",
  "updated_at": "2026-03-23T10:42:25.596880Z"
}

UI ideas: show affiliate_code, status, tier_id, hierarchy fields if you expose MLM UI.


Query Affiliate Commissions

Endpoint:

GET /v1/affiliate/commissions?status={status}&limit={limit}&offset={offset}
Authorization: Bearer <GAMER_JWT>

200 Response:

{
  "total": 0,
  "items": []
}

Example item (when present):

{
  "id": "2f2d630e-947f-4f53-a113-f8f56fc8a122",
  "tenant_id": "phoenix_test_tenant",
  "affiliate_id": "719665df-2932-4f87-92a9-aa8174d139aa",
  "referee_id": "0a36729d-e460-4e26-9ac9-fce3f1f8c944",
  "rule_id": null,
  "source_event_id": "evt_12345",
  "commission_type": "deposit.completed",
  "base_amount": 5000,
  "commission_rate": 0.05,
  "commission_amount": 250,
  "currency_id": "loyalty_points",
  "hierarchy_level": 0,
  "status": "paid",
  "paid_at": "2026-03-23T12:00:00Z",
  "created_at": "2026-03-23T11:59:00Z",
  "updated_at": "2026-03-23T12:00:00Z"
}

UI ideas: ledger table: amount, currency, status, source_event_id, date.


Query Affiliate Downline

Endpoint:

GET /v1/affiliate/downline?limit={limit}&offset={offset}
Authorization: Bearer <GAMER_JWT>

200 Response:

{
  "total": 0,
  "items": []
}

Example item:

{
  "id": "f8f0d620-6585-4d12-83d5-3077187b9f04",
  "tenant_id": "phoenix_test_tenant",
  "user_id": "partner_child_001",
  "affiliate_code": "CHILD001",
  "status": "active",
  "tier_id": null,
  "parent_affiliate_id": "719665df-2932-4f87-92a9-aa8174d139aa",
  "hierarchy_path": "/719665df-2932-4f87-92a9-aa8174d139aa/f8f0d620-6585-4d12-83d5-3077187b9f04/",
  "hierarchy_depth": 1,
  "approved_at": "2026-03-23T10:00:00Z",
  "suspended_at": null,
  "created_at": "2026-03-23T09:00:00Z",
  "updated_at": "2026-03-23T09:00:00Z"
}

Admin API (Admin Gateway)

Use ADMIN_URL + /v1/... + Authorization: Bearer <ADMIN_JWT>.
Super/client admins must pass tenant_id as a query parameter when the token does not carry a tenant.

Referral campaigns

MethodPathNotes
GET/v1/referral/campaigns?tenant_id=&active=&limit=&offset=List
POST/v1/referral/campaigns?tenant_id=Create
GET/v1/referral/campaigns/{campaign_id}?tenant_id=Detail
PUT/v1/referral/campaigns/{campaign_id}Replace (same JSON shape as POST, optional tenant_id in body)
DELETE/v1/referral/campaigns/{campaign_id}?tenant_id=Delete204 empty body

Single campaign JSON (GET/POST/PUT response):

{
  "id": "5d8532a6-0ec1-4a0d-9480-cc5b1cf81a2f",
  "tenant_id": "phoenix_test_tenant",
  "name": "JWT Test Campaign",
  "qualifying_event": "deposit.completed",
  "qualifying_filter": null,
  "referrer_rewards": { "wallet": [{ "currency_id": "loyalty_points", "amount": 10 }] },
  "referee_rewards": { "wallet": [{ "currency_id": "loyalty_points", "amount": 5 }] },
  "active": true,
  "starts_at": null,
  "ends_at": null,
  "created_at": "2026-03-23T10:42:28.340169Z",
  "updated_at": "2026-03-23T10:42:28.340169Z"
}

Affiliates

MethodPath
GET/v1/affiliates?tenant_id=&status=&limit=&offset=
POST/v1/affiliates?tenant_id=
GET/v1/affiliates/{affiliate_id}?tenant_id=
PUT/v1/affiliates/{affiliate_id}
DELETE/v1/affiliates/{affiliate_id}?tenant_id=204

PUT body: { "affiliate_code": "...", "tier_id": null, "status": "active", "tenant_id": "..." }

Affiliate tiers

MethodPath
GET/v1/affiliate/tiers?tenant_id=&limit=&offset=
POST/v1/affiliate/tiers?tenant_id=
GET/v1/affiliate/tiers/{tier_id}?tenant_id=
PUT/v1/affiliate/tiers/{tier_id}
DELETE/v1/affiliate/tiers/{tier_id}?tenant_id=204

Tier JSON:

{
  "id": "11111111-1111-1111-1111-111111111111",
  "tenant_id": "phoenix_test_tenant",
  "name": "Gold",
  "commission_rate": 0.05,
  "min_referrals": 10,
  "min_referee_revenue": 100000,
  "priority": 20,
  "active": true,
  "created_at": "2026-03-23T10:00:00Z",
  "updated_at": "2026-03-23T10:00:00Z"
}

Commission rules

MethodPath
GET/v1/affiliate/commission-rules?tenant_id=&event_type=&limit=&offset= (lists active and inactive for admin)
POST/v1/affiliate/commission-rules?tenant_id=
GET/v1/affiliate/commission-rules/{rule_id}?tenant_id=
PUT/v1/affiliate/commission-rules/{rule_id}
DELETE/v1/affiliate/commission-rules/{rule_id}?tenant_id=204

Rule JSON:

{
  "id": "22222222-2222-2222-2222-222222222222",
  "tenant_id": "phoenix_test_tenant",
  "name": "Deposit Commission",
  "event_type": "deposit.completed",
  "event_filter": null,
  "commission_field": "amount",
  "use_tier_rate": true,
  "custom_rate": null,
  "currency_id": "loyalty_points",
  "active": true,
  "created_at": "2026-03-23T10:00:00Z",
  "updated_at": "2026-03-23T10:00:00Z"
}

Affiliate hierarchy (REST)

Tenant policy (singleton) — prefer canonical paths:

MethodPathNotes
GET/v1/affiliate/hierarchy?tenant_id=404 until first PUT
PUT/v1/affiliate/hierarchy?tenant_id=Idempotent upsert (first PUT creates the row)

Deprecated alias: GET / PUT /v1/affiliate/hierarchy-config?tenant_id= — same behavior.

Response:

{
  "id": "33333333-3333-3333-3333-333333333333",
  "tenant_id": "phoenix_test_tenant",
  "max_depth": 3,
  "level_rates": [0.05, 0.02, 0.01],
  "active": true,
  "created_at": "2026-03-23T10:00:00Z",
  "updated_at": "2026-03-23T10:00:00Z"
}

Upline link (sub-resource on an affiliate):

MethodPathNotes
PUT/v1/affiliates/{affiliate_id}/hierarchy?tenant_id=Body: { "parent_affiliate_id": "<uuid>" | null }200
DELETE/v1/affiliates/{affiliate_id}/hierarchy?tenant_id=Clear upline → 204

Deprecated alias: PUT /v1/affiliates/{affiliate_id}/parent?tenant_id= — same JSON body.

Downline (collection):

MethodPath
GET/v1/affiliates/{affiliate_id}/downline?tenant_id=&limit=&offset=

See Creating Referrals — Affiliate hierarchy.

Other (unchanged)

GET /v1/referral/referrals?tenant_id=&status=&limit=&offset=

Troubleshooting 404

  1. Wrong host — Admin CRUD only on Admin Gateway, not Query Gateway.
  2. Wrong path — Must be exactly /v1/referral/... or /v1/affiliate/... (leading /v1 required).
  3. Missing tenant_id — For super/client admin tokens, add ?tenant_id=<id> to the URL.
  4. Detail routes — Use the id path segments added above (/campaigns/{id}, /affiliates/{id}, etc.).

Best Practices

  1. Use pagination for all list screens (limit / offset).
  2. Apply status filters in commission and referral dashboards.
  3. Refresh referral attribution after code application.
  4. Show clear state labels (pending, qualified, rewarded).
  5. Cache list responses briefly for UX performance.

Next Steps

On this page