Invitations & Affiliations API
An invite link is a private key; the AE behind it sits in the AffiliationTreasury contract
until someone redeems it. This API is the indexed view: which invites an address has issued and
which have been claimed. Creating or revoking an invite is a contract call, not a REST call.
All paths are relative to https://api.superhero.com. Reads need no authentication.
Endpoints
| Method & path | Query | Returns |
|---|---|---|
GET /api/invitations | order_by, order_direction, inviter, limit, page | Paginated invitation rows |
GET /api/affiliations/invites/{code} | None | One invite-code pool, by its public code |
POST /api/affiliations | None | Create a pool of invite codes |
POST /api/affiliations/invites/{code}/{provider}/{access_code} | None | Exchange a GitHub, Google or X access code for one private code |
GET /api/affiliations/preview | None | An HTML dashboard, not JSON |
GET /api/bcl-affiliation/analytics | start_date, end_date | Referral programme metrics |
GET /api/bcl-affiliation/analytics/top-inviters | start_date, end_date, limit | Leaderboard of inviters |
GET /api/bcl-affiliation/analytics/x-verification | start_date, end_date | X-verification funnel metrics |
GET /api/bcl-affiliation/analytics/x-invite | start_date, end_date | X-invite usage metrics |
GET /api/bcl-affiliation/tree | None | The whole referral graph, one node per inviter |
Each /api/bcl-affiliation/* endpoint has a /preview sibling that returns a server-rendered
HTML dashboard, not JSON.
Account-level invite counters live on the accounts endpoint:
GET /api/accounts?order_by=total_invitation_count also accepts
total_claimed_invitation_count and total_revoked_invitation_count. See
Accounts.
GET /api/invitations
| Parameter | Notes |
|---|---|
inviter | Filter to invitations sent by this ak_.... A malformed address is a 400 |
order_by | amount (default) or created_at. Anything else is a 400 |
order_direction | ASC or DESC, default DESC |
limit, page | limit defaults to 100 and must be 1–100 |
curl -s "https://api.superhero.com/api/invitations?inviter=ak_YOUR_ADDRESS&limit=100&page=1"| Field | Type | Meaning |
|---|---|---|
id | string | Row id |
tx_hash | string | th_... of the register_invitation_code call that created it |
block_height | number | Height of that call |
amount | string | The gift behind the invite, in AE as a decimal string. Do not divide by 1e18 |
sender_address | string | The inviter's ak_.... This is the field inviter filters on |
invitee_address | string or null | The wallet that redeemed it |
receiver_address | string | The ephemeral invitee keypair address encoded in the link. Unique |
status | string | pending, claimed or revoked |
claim_tx_hash | string or null | th_... of the claiming transaction |
revoke_tx_hash | string or null | th_... of the revoking transaction |
status_updated_at | string or null | When status last changed |
created_at | string | ISO 8601 |
sender | object or null | The inviter's full account row, joined in |
invitee | object or null | The redeemer's full account row, joined in |
claimed | boolean | status === 'claimed' |
claimer_address | string or null | Alias of invitee_address |
claimed_at | string or null | status_updated_at when claimed, else null |
receiver_address is the throwaway keypair in the link; invitee_address is the wallet that
redeemed it. An unclaimed invite has a receiver_address and no invitee_address. Each item
carries its claim status alongside the creation call's tx_hash, block_height and amount,
so one call covers an inviter's invites without a per-invitee middleware lookup.
/api/affiliations/*: the OAuth invite-code pool
A separate mechanism from /api/invitations, unrelated to the on-chain treasury: a pool of
pre-shared invite codes handed out one per social account, gated by an OAuth login. An
Affiliation holds a public code and an account_address; each of its codes holds one
private_code and the claimed_by identity that took it.
POST /api/affiliations
Takes no credential.
curl -s -X POST "https://api.superhero.com/api/affiliations" \
-H "Content-Type: application/json" \
-d '{"sender_address":"ak_YOUR_ADDRESS","codes":["code1","code2","code3"]}'Stores codes as the pool's private codes, generates a random ten-character public code from
[a-z0-9], and returns it:
{ "code": "k3f9xq2m1p" }sender_address is stored as the pool's account_address.
GET /api/affiliations/invites/{code}
{code} is the ten-character public code that POST /api/affiliations returned. It is
validated against /^[a-z0-9]{1,64}$/i, so anything with punctuation or a ct_/ak_ prefix is
a 400.
Returns the Affiliation row (id, account_address, code, created_at) or null (a 200
with a null body, not a 404) when no such pool exists. The child codes are not included.
POST /api/affiliations/invites/{code}/{provider}/{access_code}
provider is github, google or x (anything else is a 400; the segment is validated
against /^[a-z][a-z0-9_-]{0,31}$/i). access_code is an OAuth authorization code or access
token, which the server exchanges with that provider for a stable user id.
The server enforces one code per social identity: if provider@userId has already claimed a
code, the call is a 409 User already claimed a code. Otherwise it picks one of the pool's
unclaimed codes at random, marks it claimed_by, and returns:
{
"affiliation": { "code": "k3f9xq2m1p", "available_codes": 12 },
"invitationCode": { "private_code": "code2" }
}404 Affiliation not found when the pool does not exist; 409 No available codes for this affiliation when it is exhausted.
The on-chain treasury
Invites are created and revoked on chain. The AffiliationTreasury contract is reached through
the community factory, and its address is also affiliation_address on GET /api/factory:
const factory = await initCommunityFactory(sdk, factorySchema.address);
const treasury = await factory.affiliationTreasury();| Entrypoint | What it does |
|---|---|
register_invitation_code(addresses, redemption_fee, amount) | Funds a batch of invite keypairs. Payable: send (amount + redemption_fee) * count |
revoke_invitation_code(address) | Reclaims an unused invite |
get_invitation_code(address) | Returns [sender, amount, claimed] for one invite |
To generate invites: create count throwaway keypairs, call register_invitation_code with
their addresses, and build each link as the keypair's secret key in a URL fragment:
https://superhero.com#invite_code=<secret key>Redemption gas is covered by a fixed 0.001 AE per invite (10n ** 15n aettos) on top of the
gift amount, so the total sent is (amount + 0.001) * count plus the call's own gas. Revoking
takes the invite's address, not its secret key.
The creation side can also be read from the middleware:
GET {middlewareUrl}/v3/transactions?contract=ct_2GG42rs2FDPTXuUCWHMn98bu5Ab6mgNxY7KdGAKUNsrLqutNxZ&caller_id=ak_...Filter to tx.function === 'register_invitation_code', read the invitee addresses from argument
0 and the per-invite amount from argument 2 (in aettos; divide by 1e18), and follow
response.next to page. Use it only for an invite the indexer has not caught;
GET /api/invitations?inviter=... answers the same question in one call.
Referral analytics
The /api/bcl-affiliation/* family aggregates the same invitations table. start_date and
end_date must be exactly YYYY-MM-DD; any other format is a 400.
GET /api/bcl-affiliation/analytics
Returns series, summary, xVerification and a queryMs timing. series is one point per
day, zero-filled across the range:
date · registered · redeemed · revoked · amount_ae_registeredsummary carries total_registered, total_redeemed, total_revoked, total_outstanding,
unique_inviters, unique_invitees, unique_redeemers, total_amount_ae_registered,
avg_amount_ae_per_registered, avg_amount_ae_per_inviter, redeemed_rate and revoked_rate.
The two rates are fractions of total_registered. All amounts are in AE.
xVerification nests its own series (date, daily_new_verified, cumulative_verified) and
summary (total_verified_users), counted from link calls against the AddressLink contract.
GET /api/bcl-affiliation/analytics/top-inviters
{ items, queryMs }, limit default 10. Each item:
inviter · registered_count · redeemed_count · revoked_count · pending_count · total_amount_aeOrdered by registered_count descending, then total_amount_ae. All amounts in AE.
curl -s "https://api.superhero.com/api/bcl-affiliation/analytics/top-inviters?limit=10"GET /api/bcl-affiliation/analytics/x-verification and /x-invite
Each returns series plus summary. X-verification points are date, daily_new_verified,
cumulative_verified. X-invite points are date, created_codes, invited_users, with a
summary of total_created_codes, total_invited_users and invite_bind_rate.
GET /api/bcl-affiliation/tree
The full referral graph as one payload, with no pagination and no parameters. { items, meta }, where
items is one node per inviter:
| Field | Type | Meaning |
|---|---|---|
sender_address | string | The inviter's ak_... |
sender | object | That inviter's own stats (see below) |
invitees | array | One stats object per distinct address they invited |
amount | number | Total AE this inviter sent to invitees, in AE |
Each stats object, inviter and invitee alike, has the same fields:
address
total_invitation_count registered as inviter
total_claimed_invitation_count redeemed as inviter
total_revoked_invitation_count revoked as inviter
total_pending_invitation_count registered minus redeemed minus revoked
total_amount_ae sum of amount as inviter, in AE
total_received_invitation_count registered as inviteeAn invitee node carries only total_received_invitation_count; its other counters are 0
even when that address is itself an inviter. To walk deeper, look the invitee's address up as a
sender_address in the same items array. The structure is a flat list of one-level fans.
meta carries unique_inviters, unique_invitees, total_registered, total_redeemed,
total_revoked and total_amount_ae.
Only invitations with both a sender_address and an invitee_address are counted as edges,
so pending and revoked invites that were never redeemed contribute to meta.total_revoked but
not to any node's invitee list.
Related
- API Conventions: pagination and the write-endpoint authentication model
- Accounts API: the per-account invitation counters
- Factory & Contracts API: discovering the factory the treasury hangs off
- Node & Middleware: the middleware query behind invite creation
- Affiliation treasury: the contract itself
- Invite links: the feature from a user's point of view