Bind a referee wallet to a referrer
curl --request POST \
--url https://api-tbi.boost.xyz/v1/referrals/bind \
--header 'Content-Type: application/json' \
--data '
{
"referee": "<string>",
"referrer": "<string>",
"referrerUsername": "<string>",
"deadline": "<string>",
"signature": "<string>"
}
'import requests
url = "https://api-tbi.boost.xyz/v1/referrals/bind"
payload = {
"referee": "<string>",
"referrer": "<string>",
"referrerUsername": "<string>",
"deadline": "<string>",
"signature": "<string>"
}
headers = {"Content-Type": "application/json"}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {'Content-Type': 'application/json'},
body: JSON.stringify({
referee: '<string>',
referrer: '<string>',
referrerUsername: '<string>',
deadline: '<string>',
signature: '<string>'
})
};
fetch('https://api-tbi.boost.xyz/v1/referrals/bind', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://api-tbi.boost.xyz/v1/referrals/bind",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => json_encode([
'referee' => '<string>',
'referrer' => '<string>',
'referrerUsername' => '<string>',
'deadline' => '<string>',
'signature' => '<string>'
]),
CURLOPT_HTTPHEADER => [
"Content-Type: application/json"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"strings"
"net/http"
"io"
)
func main() {
url := "https://api-tbi.boost.xyz/v1/referrals/bind"
payload := strings.NewReader("{\n \"referee\": \"<string>\",\n \"referrer\": \"<string>\",\n \"referrerUsername\": \"<string>\",\n \"deadline\": \"<string>\",\n \"signature\": \"<string>\"\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.post("https://api-tbi.boost.xyz/v1/referrals/bind")
.header("Content-Type", "application/json")
.body("{\n \"referee\": \"<string>\",\n \"referrer\": \"<string>\",\n \"referrerUsername\": \"<string>\",\n \"deadline\": \"<string>\",\n \"signature\": \"<string>\"\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://api-tbi.boost.xyz/v1/referrals/bind")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["Content-Type"] = 'application/json'
request.body = "{\n \"referee\": \"<string>\",\n \"referrer\": \"<string>\",\n \"referrerUsername\": \"<string>\",\n \"deadline\": \"<string>\",\n \"signature\": \"<string>\"\n}"
response = http.request(request)
puts response.read_body{
"referee": "<unknown>",
"referrer": "<unknown>",
"boundAt": "<string>",
"created": true
}{
"error": "<string>",
"code": "INVALID_PARAMS",
"details": null
}{
"error": "<string>",
"code": "INVALID_PARAMS",
"details": null
}{
"error": "<string>",
"code": "INVALID_PARAMS",
"details": null
}{
"error": "<string>",
"code": "INVALID_PARAMS",
"details": null
}{
"error": "<string>",
"code": "INVALID_PARAMS",
"details": null
}{
"error": "<string>",
"code": "INVALID_PARAMS",
"details": null
}{
"error": "<string>",
"code": "INVALID_PARAMS",
"details": null
}Referrals
Bind a referee wallet to a referrer
Accepts the referee's signed referral bind. A bind is global: it ties the referee wallet to one referrer across every campaign on every chain. A referral link carries either the referrer's wallet address (referrer) or their Rabbithole username (referrerUsername) — send exactly one. A username is resolved to that account's chosen payout wallet before anything else runs (404 REFERRER_NOT_FOUND when no account has claimed it; GET /v1/referrals/usernames/{username} performs the same resolution so a signer can obtain the address first). The server canonicalizes smart-wallet proxies (Polymarket/Forkast Safe) to their owner EOA before storing. The referee signs EIP-712 typed data: domain {name: 'Boost TBI Referrals', version: '1'} (no chainId — the bind is chain-independent), type ReferralBind {referrer: address, deadline: uint256}, where referrer is the (resolved) wallet address and deadline is unix seconds at most one hour ahead; deadline travels as a decimal string because it is signed as uint256. One referrer per referee wallet — the first accepted bind wins and repeat submissions of the same bind are idempotent (created: false). A bind only earns the referrer a share in campaigns the referee first accrues rewards in after binding (no retroactive attribution; applied per campaign when the distribution is built), so existing participation never blocks a bind. Referral links only work between Rabbithole-linked wallets: the referee (403 WALLET_NOT_LINKED) and the canonicalized referrer (409 REFERRER_NOT_LINKED) must each hold an active Rabbithole wallet link, and a referrer whose wallet has ever been linked to the same Rabbithole account as the referee is a self-referral (400 SELF_REFERRAL). Referral loops are rejected (409 CYCLIC_REFERRAL): a bind whose referrer is already referred by the referee, directly (A refers B, B refers A) or through a chain of binds (A refers B, B refers C, C refers A), is refused. Alternatively, Rabbithole sessions bind without the deadline/signature fields by sending a Clerk session token as an Authorization bearer; the server verifies the token and requires the referee's active wallet link to belong to that Clerk user (401 INVALID_SESSION / 403 WALLET_NOT_LINKED otherwise).
POST
/
v1
/
referrals
/
bind
Bind a referee wallet to a referrer
curl --request POST \
--url https://api-tbi.boost.xyz/v1/referrals/bind \
--header 'Content-Type: application/json' \
--data '
{
"referee": "<string>",
"referrer": "<string>",
"referrerUsername": "<string>",
"deadline": "<string>",
"signature": "<string>"
}
'import requests
url = "https://api-tbi.boost.xyz/v1/referrals/bind"
payload = {
"referee": "<string>",
"referrer": "<string>",
"referrerUsername": "<string>",
"deadline": "<string>",
"signature": "<string>"
}
headers = {"Content-Type": "application/json"}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {'Content-Type': 'application/json'},
body: JSON.stringify({
referee: '<string>',
referrer: '<string>',
referrerUsername: '<string>',
deadline: '<string>',
signature: '<string>'
})
};
fetch('https://api-tbi.boost.xyz/v1/referrals/bind', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://api-tbi.boost.xyz/v1/referrals/bind",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => json_encode([
'referee' => '<string>',
'referrer' => '<string>',
'referrerUsername' => '<string>',
'deadline' => '<string>',
'signature' => '<string>'
]),
CURLOPT_HTTPHEADER => [
"Content-Type: application/json"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"strings"
"net/http"
"io"
)
func main() {
url := "https://api-tbi.boost.xyz/v1/referrals/bind"
payload := strings.NewReader("{\n \"referee\": \"<string>\",\n \"referrer\": \"<string>\",\n \"referrerUsername\": \"<string>\",\n \"deadline\": \"<string>\",\n \"signature\": \"<string>\"\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.post("https://api-tbi.boost.xyz/v1/referrals/bind")
.header("Content-Type", "application/json")
.body("{\n \"referee\": \"<string>\",\n \"referrer\": \"<string>\",\n \"referrerUsername\": \"<string>\",\n \"deadline\": \"<string>\",\n \"signature\": \"<string>\"\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://api-tbi.boost.xyz/v1/referrals/bind")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["Content-Type"] = 'application/json'
request.body = "{\n \"referee\": \"<string>\",\n \"referrer\": \"<string>\",\n \"referrerUsername\": \"<string>\",\n \"deadline\": \"<string>\",\n \"signature\": \"<string>\"\n}"
response = http.request(request)
puts response.read_body{
"referee": "<unknown>",
"referrer": "<unknown>",
"boundAt": "<string>",
"created": true
}{
"error": "<string>",
"code": "INVALID_PARAMS",
"details": null
}{
"error": "<string>",
"code": "INVALID_PARAMS",
"details": null
}{
"error": "<string>",
"code": "INVALID_PARAMS",
"details": null
}{
"error": "<string>",
"code": "INVALID_PARAMS",
"details": null
}{
"error": "<string>",
"code": "INVALID_PARAMS",
"details": null
}{
"error": "<string>",
"code": "INVALID_PARAMS",
"details": null
}{
"error": "<string>",
"code": "INVALID_PARAMS",
"details": null
}Headers
Optional partner attribution reference ID. Values are manually assigned by Boost and must be at most 128 characters.
Required string length:
1 - 128Pattern:
^[A-Za-z0-9][A-Za-z0-9._:-]*$Body
application/json