Affiliate Service API Integration Guide
NOTE: API documentation is currently under development
1) Scope
AffiliateService - service for affiliate integration.
This API is dedicated to affiliate partners and designed for production integrations.
Key points:
- API is public/open (no auth headers required).
affiliateIdis generated by the service. It may look like an EVM wallet address, but is not the same value as the original wallet used during registration.affiliateIdis the primary identifier for all subsequent calls after registration.- After registration the affiliate is activated asynchronously (on-chain funding
- fee-pool registration). Use
isActivatedfrom register/lookup responses before relying on fee balance or withdrawals.
- fee-pool registration). Use
- Registration accepts a required
evmWalletand an optionalbtcWallet. - Withdrawal modes:
- EVM-only (no
btcWallet): when fee balance reaches the configured threshold (default 100 USD), the system automatically withdraws to theevmWallet(default payout token:usdt_bnb). - With
btcWallet: automatic withdrawal is disabled. Manual withdrawal becomes available once the fee balance reaches the configured threshold (default 100 USD). The partner callsPOST /affiliates/{affiliateId}/withdraw-feesand chooses the payout token (usdt_bnb,usdc_bnb,usdt_eth,usdc_eth,eth, orbtc). BTC payouts go tobtcWallet; other tokens go toevmWallet.
- EVM-only (no
2) Core terms
evmWallet: external EVM wallet provided by affiliate during registration (required). Used for non-BTC payouts.btcWallet: optional Bitcoin payout address provided at registration (mainnet: legacy1…/3…or Bech32bc1q…/bc1p…). Enables manual multi-token withdrawal and BTC payouts.affiliateId: service-generated affiliate identifier (external-facing ID for API calls).isActivated: whether on-chain activation finished successfully. Fee accrual and withdrawals require an activated affiliate.l1TxId: swap transaction identifier on L1 side.
3) Data format and transport conventions
- Base URL (example):
https://api-affiliate.pactswap.io/api/v1 - Content type:
application/json - Successful responses use
2xxstatus codes. - Error responses use
4xx/5xxstatus codes and unified error envelope. - Address/id fields are case-insensitive in requests, but response values should be treated as canonical (except Base58 BTC addresses, which keep original casing).
- Monetary values are returned as string decimals to avoid precision loss.
- For swap endpoints, numeric token amounts (for example,
amountFrom) are in the smallest token units (not human-readable units): satoshis for BTC, wei for ETH/BNB/POL, etc. Example:amountFrom=100for BTC means100 satoshis, not100 BTC.
4) Versioning policy
- API version is part of URL:
/api/v1. - Backward-compatible additions may include new optional fields.
- Breaking changes are released under a new version path (for example,
/api/v2).
5) Error response format
Unified error envelope (recommended):
{
"error": {
"code": "AFFILIATE_NOT_FOUND",
"message": "Affiliate does not exist",
"details": {}
}
}Common error codes:
INVALID_ARGUMENTAFFILIATE_ALREADY_EXISTSAFFILIATE_NOT_FOUNDAFFILIATE_NOT_ACTIVATEDMANUAL_WITHDRAWAL_NOT_ALLOWEDBELOW_THRESHOLDSWAP_NOT_FOUNDRATE_LIMITEDINTERNAL_ERROR
6) REST endpoints
6.1 Register affiliate
- Method/Path:
POST /affiliates - Purpose: register new affiliate by
evmWallet(and optionalbtcWallet) and generateaffiliateId. Activation starts asynchronously; new affiliates returnisActivated: false.
Request body:
{
"evmWallet": "0x1234567890abcdef1234567890abcdef12345678",
"btcWallet": "bc1qxy2kgdygjrsqtzq2n0yrf2493p83kkfjhx0wlh"
}btcWallet is optional. Omit it for EVM-only affiliates (auto-withdrawal).
Example to register a new affiliate by EVM wallet (optional BTC):
BASE_URL="https://api-affiliate.pactswap.io/api/v1"
curl -sS -X POST "$BASE_URL/affiliates" \
-H "Content-Type: application/json" \
-d '{
"evmWallet": "0x1234567890abcdef1234567890abcdef12345678",
"btcWallet": "bc1qxy2kgdygjrsqtzq2n0yrf2493p83kkfjhx0wlh"
}'Response 201:
{
"affiliateId": "0x9a8b7c6d5e4f3210abcdeffedcba012345678901",
"isActivated": false
}Possible errors:
400 INVALID_ARGUMENT(invalidevmWallet/btcWalletformat)409 AFFILIATE_ALREADY_EXISTS(EVM or BTC wallet already registered)
6.2 Get affiliate by wallet
- Method/Path:
GET /affiliates/by-wallet/{evmWallet} - Purpose: resolve existing
affiliateId(and activation status) by source EVM wallet.
Example to resolve affiliateId by partner wallet:
BASE_URL="https://api-affiliate.pactswap.io/api/v1"
EVM_WALLET="0x1234567890abcdef1234567890abcdef12345678"
curl -sS "$BASE_URL/affiliates/by-wallet/$EVM_WALLET"Response 200:
{
"affiliateId": "0x9a8b7c6d5e4f3210abcdeffedcba012345678901",
"isActivated": true
}Possible errors:
400 INVALID_ARGUMENT(invalid wallet format)404 AFFILIATE_NOT_FOUND
6.3 Get fee balance
- Method/Path:
GET /affiliates/{affiliateId}/fee-balance - Purpose: get current fee balance in USD.
Example to get current affiliate fee balance:
BASE_URL="https://api-affiliate.pactswap.io/api/v1"
AFFILIATE_ID="0x9a8b7c6d5e4f3210abcdeffedcba012345678901"
curl -sS "$BASE_URL/affiliates/$AFFILIATE_ID/fee-balance"Response 200:
{
"balanceUsd": "1234.560000"
}Possible errors:
400 INVALID_ARGUMENT(invalidaffiliateIdformat)404 AFFILIATE_NOT_FOUND
6.4 Manual fee withdrawal
- Method/Path:
POST /affiliates/{affiliateId}/withdraw-fees - Purpose: manually withdraw accumulated fees. Only available for affiliates
registered with a
btcWallet. Affiliate must be activated and fee balance must be at or above the withdrawal threshold.
Request body:
{
"token": "btc"
}Supported token values:
token | Payout chain | Destination wallet |
|---|---|---|
usdt_bnb | BNB | evmWallet |
usdc_bnb | BNB | evmWallet |
usdt_eth | ETH | evmWallet |
usdc_eth | ETH | evmWallet |
eth | ETH | evmWallet |
btc | BTC | btcWallet |
Example:
BASE_URL="https://api-affiliate.pactswap.io/api/v1"
AFFILIATE_ID="0x9a8b7c6d5e4f3210abcdeffedcba012345678901"
curl -sS -X POST "$BASE_URL/affiliates/$AFFILIATE_ID/withdraw-fees" \
-H "Content-Type: application/json" \
-d '{
"token": "btc"
}'Response 201 / 200:
{
"recordId": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"token": "btc",
"feesUsdVirtual": 105,
"l1Amount": "65000",
"l1Address": "bc1qxy2kgdygjrsqtzq2n0yrf2493p83kkfjhx0wlh",
"tokenDecimals": 8,
"orderEmbedId": "embed-id-example",
"orderL2Txid": "l2-txid-example"
}Notes:
l1Amountis in the payout token’s smallest units (derived from fee USD notional and current token USD price).orderEmbedId/orderL2Txididentify the on-chain payout order created by the service.
Possible errors:
400 INVALID_ARGUMENT(invalidaffiliateId/token)400 MANUAL_WITHDRAWAL_NOT_ALLOWED(affiliate has nobtcWallet; use auto-withdrawal instead)400 BELOW_THRESHOLD(detailsincludesbalanceUsdandthresholdUsd)404 AFFILIATE_NOT_FOUND422 AFFILIATE_NOT_ACTIVATED500 INTERNAL_ERROR
6.5 List affiliate swaps
- Method/Path:
GET /affiliates/{affiliateId}/swaps - Purpose: paginated list of swaps attributed to the affiliate. Use each
l1TxIdwithGET /swaps/{l1TxId}(§ 6.6) orGET /getL1TxInfo(§ 6.7) when you need fee amounts or ledger allocations.
Query params:
offset(number, optional, default0) — number of items to skip. Must be≥ 0.count(number, optional, default10) — page size. Must be1..100.
Example:
BASE_URL="https://api-affiliate.pactswap.io/api/v1"
AFFILIATE_ID="0x9a8b7c6d5e4f3210abcdeffedcba012345678901"
curl -sS "$BASE_URL/affiliates/$AFFILIATE_ID/swaps" \
--get \
--data-urlencode "offset=0" \
--data-urlencode "count=10"Response 200 (example):
{
"swaps": [
{
"l1TxId": "261f445e311b9ad5e0d127b3bd5567a468e54d214040e4933e43eb92a40113a5",
"fromToken": "BNB",
"feeBips": 15,
"timestamp": 1782379394000
},
{
"l1TxId": "2397ff0a4359a3e3bcce9bef5a1a6ff0556dd8a3b31e9b1de9b8306c20fd2934",
"fromToken": "USDT_BNB",
"feeBips": 15,
"timestamp": 1780045460000
},
{
"l1TxId": "4ce85e1e716c95861adc067e02bb4bc324925701aa1799b6ec3f3c4562c20466",
"fromToken": "BTC",
"feeBips": 40,
"timestamp": 1778521106000
}
],
"offset": 0,
"count": 10,
"hasMore": true
}Notes:
fromTokenis the source token of the swap (for exampleBNB,BTC,USDT_BNB,USDT_ETH).feeBipsis the affiliate fee for that swap in basis points (1 bp = 0.01%).timestampis the swap finalization time as a Unix epoch in milliseconds (not the time the swap was initiated).offset/countin the response echo the requested pagination.hasMore: truemeans another page is available; request the next page withoffset = offset + count.
Possible errors:
400 INVALID_ARGUMENT(invalidaffiliateIdformat,offset < 0, orcountoutside1..100)404 AFFILIATE_NOT_FOUND
6.6 Get swap info
- Method/Path:
GET /swaps/{l1TxId} - Purpose: returns consolidated swap fee info in a single response — total
acquired fees and swap kind — without a per-affiliate breakdown. Use
GET /getL1TxInfo(§ 6.7) when you need individual affiliate allocations.
Example to get swap info by L1 transaction id:
BASE_URL="https://api-affiliate.pactswap.io/api/v1"
L1TXID="0xaaaabbbbccccddddeeeeffff1111222233334444555566667777888899990000"
curl -sS "$BASE_URL/swaps/$L1TXID"Response 200 (normal case):
{
"kind": "normal",
"feesAcquiredUsdt": "245.100000",
"feeBips": 30
}Response 200 (reversed pact case):
{
"kind": "reversed_pact"
}Possible errors:
400 INVALID_ARGUMENT(invalidl1TxIdformat)404 SWAP_NOT_FOUND
6.7 Get L1 tx fee ledger info
- Method/Path:
GET /getL1TxInfo - Purpose: look up affiliate fee allocation for a completed L1 swap from the on-chain fee ledger (preferred for reconciliation).
Query params:
l1TxId(string, required) — L1 transaction id (with or without0xprefix)
Example:
BASE_URL="https://api-affiliate.pactswap.io/api/v1"
L1TXID="aaaabbbbccccddddeeeeffff1111222233334444555566667777888899990000"
curl -sS "$BASE_URL/getL1TxInfo" \
--get \
--data-urlencode "l1TxId=$L1TXID"Response 200 (example):
{
"l1TxId": "aaaabbbbccccddddeeeeffff1111222233334444555566667777888899990000",
"oracles": {
"l1TokenToUsd": { "rate": "350000000000", "decimals": 8 },
"usdtBnbToUsd": { "rate": "100000000", "decimals": 8 }
},
"affiliates": [
{
"affiliateId": "0x9a8b7c6d5e4f3210abcdeffedcba012345678901",
"basisPoints": 25,
"amountRaw": "612750000000000000",
"decimals": 18
}
]
}Notes:
oracles/affiliatesmay be omitted when the ledger claim has no such payload.amountRawis the affiliate fee share in virtual USDT (BNB) units atdecimalsprecision.
Possible errors:
400 INVALID_ARGUMENT(missingl1TxId)404 SWAP_NOT_FOUND500 INTERNAL_ERROR
6.8 Get swap quotes by send amount (quotes)
- Method/Path:
GET /pactswap_cm/getSwapQuotesByAmountFromonhttps://api.pactswap.io - Purpose: retrieve multiple swap quotes for a given
amountFrom(compare rates from different routes).
Query params:
from(string, required) — L2 C1 contract id for the source token.to(string, required) — L2 C2 contract id for the destination token.amountFrom(number, required) — amount in smallest units.fromType(string, optional) — source chain/type (for example,eth,btc,bnb).toType(string, optional) — destination chain/type.affiliates(array, optional) — affiliate fee recipients. In query strings use indexed fields, for exampleaffiliates[0][affiliateId]andaffiliates[0][bips]. Use youraffiliateIdfrom registration.slippageBips(number, optional) — slippage tolerance in basis points (1 bp = 0.01%, so100= 1%). Minimum50when provided. Quotes includeminAmountToderived from this.
Contract ids and presets are deployment-specific. Replace the placeholders below with values from your environment before calling production.
Example: request quotes to swap 1 ETH to BNB:
PACTSWAP_BASE_URL="https://api.pactswap.io"
# ETH (source): C1 base + destination C2 maker for BNB — replace with your deployment ids
FROM_C1="0x3f4031b087cb09891360a0b7aada49359eaabd09713979c190effc5b7ff928f6"
TO_C2="0x20ada6149b7480fc734c0ac2482d139e1febe1c5c01f9d7708b32ea986f691f7"
curl -sS -g "$PACTSWAP_BASE_URL/pactswap_cm/getSwapQuotesByAmountFrom" \
--get \
--data-urlencode "from=$FROM_C1" \
--data-urlencode "to=$TO_C2" \
--data-urlencode "fromType=eth" \
--data-urlencode "toType=bnb" \
--data-urlencode "amountFrom=1000000000000000000" \
--data-urlencode "slippageBips=100" \
--data-urlencode "affiliates[0][affiliateId]=0x9a8b7c6d5e4f3210abcdeffedcba012345678901" \
--data-urlencode "affiliates[0][bips]=25" \
--data-urlencode "affiliates[1][affiliateId]=0x5D3e2B7C9a1F4c8E0b6A7D2C1E9f3A4b8C6d0F12" \
--data-urlencode "affiliates[1][bips]=40"-g tells curl not to treat [ / ] in affiliates[0][...] as URL glob
characters.
Response 200 (example):
[
{
"amountTo": 50000000,
"minAmountTo": 49500000,
"route": "0xC0ffee254729296a45a3885639AC7E10F9d54979",
"lp": "0xC0ffee254729296a45a3885639AC7E10F9d54979",
"estimatedSwapTimeSeconds": 120
}
]Notes:
amountFromis always provided in the smallest units of the source token (for example, satoshis for BTC, wei for ETH/BNB/POL).- Example:
amountFrom=100for BTC means100 satoshis, not100 BTC. route/lp— liquidity provider identifier from the quote. Pass it to compose asrouteorlp.minAmountTo— lower bound on the destination amount implied by the quotedamountToand yourslippageBips(same smallest units asamountTo).estimatedSwapTimeSeconds— approximate expected swap duration in seconds (not a hard guarantee).
6.9 Compose swap transaction by send amount (compose)
- Method/Path:
GET /pactswap_cm/composeSwapTxByAmountFromonhttps://api.pactswap.io - Purpose: compose an unsigned L1 transaction based on
amountFrom. The affiliate/user must sign this transaction with their wallet, then broadcast it to the source chain.
Query params (required for EVM swaps):
fromType,toType(string)fromWalletAddress,toWalletAddress(string) — valid addresses for the respective chainsfromC1ContractId,fromC2ContractId,toC1ContractId,toC2ContractId(string) — L2 contract ids for source/destination tokensamountFrom(number)preset(number) — swap routing preset for the destination chain and address format (deployment-specific)routeorlp(string) — liquidity provider id from the quote response
Query params (optional):
affiliates(array) — same indexed query format as in § 6.8slippageBips(number, optional) — same meaning as for quotes (≥50when provided); see § 6.8
Query params (UTXO-based source chains only):
fromPublicKey(string, required for BTC/LTC/DOGE)utxos(string, required for BTC/LTC/DOGE) — JSON string with UTXOs
Example: compose an unsigned EVM transaction to swap 1 ETH to BNB (use route
from § 6.8):
PACTSWAP_BASE_URL="https://api.pactswap.io"
FROM_C1="0x3f4031b087cb09891360a0b7aada49359eaabd09713979c190effc5b7ff928f6"
FROM_C2="0xe2dcfe88387f5dbc8ec583727cf4280462ceb356be3199d320ed818089f8a693"
TO_C1="0x6f20d1802288c90d2a044d3f7afae73fda261b27d85c0e01fdaaf3199b2c0e85"
TO_C2="0x20ada6149b7480fc734c0ac2482d139e1febe1c5c01f9d7708b32ea986f691f7"
ROUTE="0xC0ffee254729296a45a3885639AC7E10F9d54979" # from quotes[].route or quotes[].lp
PRESET="9007199254740257" # deployment-specific; depends on toType + toWalletAddress format
curl -sS -g "$PACTSWAP_BASE_URL/pactswap_cm/composeSwapTxByAmountFrom" \
--get \
--data-urlencode "fromType=eth" \
--data-urlencode "toType=bnb" \
--data-urlencode "fromWalletAddress=0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045" \
--data-urlencode "toWalletAddress=0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045" \
--data-urlencode "amountFrom=1000000000000000000" \
--data-urlencode "fromC1ContractId=$FROM_C1" \
--data-urlencode "fromC2ContractId=$FROM_C2" \
--data-urlencode "toC1ContractId=$TO_C1" \
--data-urlencode "toC2ContractId=$TO_C2" \
--data-urlencode "preset=$PRESET" \
--data-urlencode "route=$ROUTE" \
--data-urlencode "slippageBips=100" \
--data-urlencode "affiliates[0][affiliateId]=0x9a8b7c6d5e4f3210abcdeffedcba012345678901" \
--data-urlencode "affiliates[0][bips]=25" \
--data-urlencode "affiliates[1][affiliateId]=0x5D3e2B7C9a1F4c8E0b6A7D2C1E9f3A4b8C6d0F12" \
--data-urlencode "affiliates[1][bips]=40"Response 200 (example):
{
"rawTx": "0x123..."
}Notes:
rawTxis an unsigned transaction payload returned by the API for client-side signing.- Signing and broadcasting are performed by the affiliate/user wallet flow (outside this API endpoint).
7) TypeScript interfaces for affiliate clients
export type AddressLike = string;
export type DecimalString = string;
export type WithdrawalToken =
| 'usdt_bnb'
| 'usdc_bnb'
| 'usdt_eth'
| 'usdc_eth'
| 'eth'
| 'btc';
export interface RegisterAffiliateRequest {
evmWallet: AddressLike;
btcWallet?: string;
}
export interface RegisterAffiliateResponse {
affiliateId: AddressLike;
isActivated: boolean;
}
export interface GetAffiliateResponse {
affiliateId: AddressLike;
isActivated: boolean;
}
export interface GetFeeBalanceResponse {
balanceUsd: DecimalString;
}
export interface WithdrawFeesRequest {
token: WithdrawalToken;
}
export interface WithdrawFeesResponse {
recordId: string;
token: WithdrawalToken;
feesUsdVirtual: number;
l1Amount: string;
l1Address: string;
tokenDecimals: number;
orderEmbedId: string | null;
orderL2Txid: string | null;
}
export interface AffiliateSwapItem {
l1TxId: string;
fromToken: string;
feeBips: number;
timestamp: number; // swap finalization, Unix epoch ms
}
export interface ListAffiliateSwapsResponse {
swaps: AffiliateSwapItem[];
offset: number;
count: number;
hasMore: boolean;
}
export interface SwapInfoNormal {
kind: 'normal';
feesAcquiredUsdt: DecimalString;
feeBips: number;
}
export interface SwapInfoReversedPact {
kind: 'reversed_pact';
}
export type GetSwapInfoResponse = SwapInfoNormal | SwapInfoReversedPact;
export interface GetL1TxInfoResponse {
l1TxId: string;
oracles?: {
l1TokenToUsd: { rate: string; decimals: number };
usdtBnbToUsd: { rate: string; decimals: number };
};
affiliates?: {
affiliateId: string;
basisPoints: number;
amountRaw: string;
decimals: number;
}[];
}
export interface ApiErrorResponse {
error: {
code: string;
message: string;
details?: Record<string, unknown>;
};
}8) Integration flow (recommended)
- Call
POST /affiliateswith partnerevmWallet(and optionalbtcWalletif you need manual / BTC withdrawals).
BASE_URL="https://api-affiliate.pactswap.io/api/v1"
curl -sS -X POST "$BASE_URL/affiliates" \
-H "Content-Type: application/json" \
-d '{
"evmWallet": "0x1234567890abcdef1234567890abcdef12345678",
"btcWallet": "bc1qxy2kgdygjrsqtzq2n0yrf2493p83kkfjhx0wlh"
}'- Store returned
affiliateIdas primary identifier for all next API calls. PollGET /affiliates/by-wallet/{evmWallet}untilisActivatedistruebefore fee balance / withdrawal flows.
# Example of storing returned affiliateId for next requests
AFFILIATE_ID="0x9a8b7c6d5e4f3210abcdeffedcba012345678901"
EVM_WALLET="0x1234567890abcdef1234567890abcdef12345678"
curl -sS "$BASE_URL/affiliates/by-wallet/$EVM_WALLET"- For accounting UI, periodically call:
GET /affiliates/{affiliateId}/fee-balanceGET /affiliates/{affiliateId}/swaps(paginated swap history)
curl -sS "$BASE_URL/affiliates/$AFFILIATE_ID/fee-balance"
curl -sS "$BASE_URL/affiliates/$AFFILIATE_ID/swaps" \
--get \
--data-urlencode "offset=0" \
--data-urlencode "count=10"- Withdraw fees:
- EVM-only affiliates: no action — auto-withdrawal runs when the threshold is reached.
- Affiliates with
btcWallet: callPOST /affiliates/{affiliateId}/withdraw-feeswith the desiredtoken.
curl -sS -X POST "$BASE_URL/affiliates/$AFFILIATE_ID/withdraw-fees" \
-H "Content-Type: application/json" \
-d '{"token":"usdt_bnb"}'- For swap audit/reconciliation:
- list attributed swaps via
GET /affiliates/{affiliateId}/swaps - prefer
GET /getL1TxInfo?l1TxId=...for eachl1TxId - optionally
GET /swaps/{l1TxId}
- list attributed swaps via
L1TXID="aaaabbbbccccddddeeeeffff1111222233334444555566667777888899990000"
curl -sS "$BASE_URL/getL1TxInfo" --get --data-urlencode "l1TxId=$L1TXID"- Get swap quotes on Pact Swap API (pick
route/lpfrom the response):GET https://api.pactswap.io/pactswap_cm/getSwapQuotesByAmountFrom- pass your
affiliateIdfrom step 2 inaffiliates; setslippageBipsforminAmountToon quotes
PACTSWAP_BASE_URL="https://api.pactswap.io"
FROM_C1="0x3f4031b087cb09891360a0b7aada49359eaabd09713979c190effc5b7ff928f6"
TO_C2="0x20ada6149b7480fc734c0ac2482d139e1febe1c5c01f9d7708b32ea986f691f7"
# `amountFrom` is in smallest units (wei/satoshi/etc), not whole coins.
curl -sS -g "$PACTSWAP_BASE_URL/pactswap_cm/getSwapQuotesByAmountFrom" \
--get \
--data-urlencode "from=$FROM_C1" \
--data-urlencode "to=$TO_C2" \
--data-urlencode "fromType=eth" \
--data-urlencode "toType=bnb" \
--data-urlencode "amountFrom=1000000000000000000" \
--data-urlencode "slippageBips=100" \
--data-urlencode "affiliates[0][affiliateId]=$AFFILIATE_ID" \
--data-urlencode "affiliates[0][bips]=25" \
--data-urlencode "affiliates[1][affiliateId]=0x5D3e2B7C9a1F4c8E0b6A7D2C1E9f3A4b8C6d0F12" \
--data-urlencode "affiliates[1][bips]=40"- Compose swap tx by selected route on Pact Swap API:
GET https://api.pactswap.io/pactswap_cm/composeSwapTxByAmountFrom- pass
route(orlp) from step 6, all four contract ids,preset,affiliates, andslippageBips(same values as step 6) - endpoint returns an unsigned tx payload; sign it in wallet flow and then broadcast
PACTSWAP_BASE_URL="https://api.pactswap.io"
FROM_C1="0x3f4031b087cb09891360a0b7aada49359eaabd09713979c190effc5b7ff928f6"
FROM_C2="0xe2dcfe88387f5dbc8ec583727cf4280462ceb356be3199d320ed818089f8a693"
TO_C1="0x6f20d1802288c90d2a044d3f7afae73fda261b27d85c0e01fdaaf3199b2c0e85"
TO_C2="0x20ada6149b7480fc734c0ac2482d139e1febe1c5c01f9d7708b32ea986f691f7"
ROUTE="0xC0ffee254729296a45a3885639AC7E10F9d54979"
PRESET="9007199254740257"
curl -sS -g "$PACTSWAP_BASE_URL/pactswap_cm/composeSwapTxByAmountFrom" \
--get \
--data-urlencode "fromType=eth" \
--data-urlencode "toType=bnb" \
--data-urlencode "fromWalletAddress=0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045" \
--data-urlencode "toWalletAddress=0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045" \
--data-urlencode "amountFrom=1000000000000000000" \
--data-urlencode "fromC1ContractId=$FROM_C1" \
--data-urlencode "fromC2ContractId=$FROM_C2" \
--data-urlencode "toC1ContractId=$TO_C1" \
--data-urlencode "toC2ContractId=$TO_C2" \
--data-urlencode "preset=$PRESET" \
--data-urlencode "route=$ROUTE" \
--data-urlencode "slippageBips=100" \
--data-urlencode "affiliates[0][affiliateId]=$AFFILIATE_ID" \
--data-urlencode "affiliates[0][bips]=25" \
--data-urlencode "affiliates[1][affiliateId]=0x5D3e2B7C9a1F4c8E0b6A7D2C1E9f3A4b8C6d0F12" \
--data-urlencode "affiliates[1][bips]=40"Operational recommendations:
- Treat
POST /affiliatesas idempotent byevmWalleton client side (retry-safe logic). Note: re-registering an existing wallet returns409 AFFILIATE_ALREADY_EXISTS— resolve viaGET /affiliates/by-wallet/.... - Wait for
isActivated: truebefore withdrawal attempts. - Use exponential backoff for temporary
5xxfailures.
9) Operational guidance for affiliates
9.1 Idempotency and retries
- For
POST /affiliates, client should implement retry-safe behavior keyed byevmWallet(andbtcWalletwhen provided). - Recommended retry strategy for transient failures (
5xx, network timeout): exponential backoff with jitter.
9.2 Rate limits
- Default partner limit recommendation:
60 requests/minuteper IP. - If response is
429 RATE_LIMITED, retry after backoff. - Clients should cache stable lookup data (
affiliateIdresolution) to reduce request volume.
9.3 Timeouts and polling
- Recommended client timeout per request:
10s. - For operational dashboards, polling interval for balance/history:
15-60sdepending on UI needs. - After registration, poll activation status every
15-30suntilisActivatedbecomestrue(activation involves on-chain funding and may take up to a few minutes).
9.4 Withdrawal checklist
- Decide at registration whether you need BTC / multi-token payouts (
btcWallet) or automatic EVM payouts (omitbtcWallet). - EVM-only: monitor
fee-balance; payouts happen automatically above threshold. - With
btcWallet: callwithdraw-feeswhen balance ≥ threshold; choosetokencarefully (btcrequires a registeredbtcWallet). - Handle
BELOW_THRESHOLDandAFFILIATE_NOT_ACTIVATEDexplicitly.
9.5 Production readiness checklist (partner side)
- Store
affiliateIdas primary partner key. - Persist and surface
isActivated. - Handle both swap variants:
normalandreversed_pact. - Prefer
GET /affiliates/{affiliateId}/swapsfor paginated swap history; page withoffset/countwhilehasMoreistrue. - Prefer
getL1TxInfofor fee reconciliation on completed swaps. - Implement structured handling for documented error codes.
- Add observability for request id, latency, non-2xx responses, and retry attempts.
Was this documentation helpful? Any suggestions?