Skip to Content

activatePromoCode

Activates a promo code for a bettor and returns the freebet it grants. Throws a PromoCodeError when the code is rejected.

ℹ️

You can find more information Here.

Usage

import { activatePromoCode, isPromoCodeError } from '@azuro-org/toolkit' try { const freebet = await activatePromoCode({ chainId: 137, code: 'SUMMER26', account: '0x...', // bettor's address affiliate: '0x...', // your affiliate address }) } catch (error) { if (isPromoCodeError(error) && error.code === 'bonus.promo_code_already_activated') { // tell the bettor the code was already used } }

Props

type ActivatePromoCodeParams = { chainId: ChainId code: string // trimmed and upper-cased before sending, up to 32 characters account: Address // bettor's address affiliate: Address // your affiliate address, must equal the address of the code's pool }
type ChainId = | 137 // Polygon | 80002 // Polygon Amoy | 8453 // Base | 84532 // Base Sepolia | 100 // Gnosis | 88888 // Chiliz | 88882 // Chiliz Spicy | 97 // BSC Testnet | 56 // BSC import { type Address } from 'viem'
ℹ️

chainId only selects the API environment. Development chains (Polygon Amoy, Chiliz Spicy, Base Sepolia, BSC Testnet) call https://dev-api.onchainfeed.org/api/v1/public, every other chain calls https://api.onchainfeed.org/api/v1/public.

The chainId of the returned freebet is the freebet’s own chain, taken from the pool the code pays from. It can differ from the chainId you passed: activating a code that belongs to a Base pool with chainId: 137 returns a freebet with chainId: 8453.

Return Value

type ActivatePromoCodeResult = Freebet

amount is in tokens (for example "5"), and expiresAt, usedAt and createdAt are timestamps in milliseconds.

enum BonusType { FreeBet = 'FreeBet', } enum BonusStatus { Used = 'Used', Available = 'Available', } enum FreebetType { OnlyWin = 'OnlyWin', AllWin = 'AllWin', } enum BetRestrictionType { Ordinar = 'Ordinar', Combo = 'Combo', } enum EventRestrictionState { Live = 'Live', Prematch = 'Prematch', } type BonusBase = { id: string type: BonusType amount: string status: BonusStatus chainId: ChainId expiresAt: number usedAt: number createdAt: number publicCustomData: Record<string, string> | null } type Freebet = { type: BonusType.FreeBet params: { isBetSponsored: boolean isFeeSponsored: boolean isSponsoredBetReturnable: boolean } settings: { type: FreebetType feeSponsored: boolean betRestriction: { type: BetRestrictionType | undefined minOdds: string maxOdds: string | undefined } eventRestriction: { state: EventRestrictionState | undefined eventFilter?: { exclude: boolean filter: [ { sportId: string leagues: string[] markets: { marketId: number gamePeriodId: number gameTypeId: number }[] } ] } } periodOfValidityMs: number } } & BonusBase

Errors

Every non-OK HTTP response, and every successful response whose body cannot be read as a freebet, is thrown as a PromoCodeError. Branch on code, never on message.

class PromoCodeError extends Error { name: 'PromoCodeError' code: PromoCodeErrorCode status?: number // HTTP status of the response, when there was one cause?: unknown // the original error, when the failure came from an unreadable response body }
type PromoCodeErrorCode = | 'bonus.promo_code_not_found' | 'bonus.promo_code_deactivated' | 'bonus.promo_code_expired' | 'bonus.promo_code_unavailable' | 'bonus.promo_code_affiliate_mismatch' | 'bonus.promo_code_already_activated' | 'bonus.promo_code_limit_reached' | 'bonus.promo_code_busy' | 'bonus.activate_promo_code_error' | 'unknown'
CodeWhen it happensSuggested message
bonus.promo_code_not_foundNo such code. Also any code that cannot exist: characters other than A-Z, 0-9, -, _The code doesn’t exist
bonus.promo_code_deactivatedThe operator deactivated the codeThe code is no longer active
bonus.promo_code_expiredThe code’s expiry time has been reachedThe code has expired
bonus.promo_code_unavailableThe code’s product or operator is not activeThe code is not available right now
bonus.promo_code_affiliate_mismatchThe code’s pool address is not the affiliate you sentThe code is not valid on this site
bonus.promo_code_already_activatedThis address already activated this codeThe code was already used
bonus.promo_code_limit_reachedThe code has used up its activation limitAll activations of this code are used up
bonus.promo_code_busyAnother activation of the same code is being processed. Safe to retryTry again in a moment
bonus.activate_promo_code_errorThe activation failed on the server for another reasonSomething went wrong, try again later
unknownAnything else: input rejected by validation (an empty code, a code longer than 32 characters, a malformed address), a server error, a reason this toolkit version does not know, or a successful response whose body cannot be readGeneric error

For unknown, status and the server’s message are kept on the error. For an unreadable successful response, status is the response status and cause holds the original error. Limit the input to 32 characters, longer codes are rejected by validation and come back as unknown.

When several reasons apply, the server reports the first one in this order: deactivated, expired, unavailable, affiliate mismatch, already activated, limit reached.

isPromoCodeError(error) is a type guard that checks name === 'PromoCodeError' and a known code instead of using instanceof, so it keeps working when two copies of the toolkit end up in one bundle.

⚠️

A network failure (a rejected fetch) is not a PromoCodeError. It rejects with the underlying error, so isPromoCodeError returns false for it. Treat it as a generic failure.