REST API
Market Data Integration
To reduce latency from intermediary forwarding when retrieving market data, partners should connect directly to the official APIs of Hyperliquid and Stove to obtain market data from each platform. Refer to the following official Open API documentation for integration details and interface requirements:
Request methods, authentication requirements, and rate limits for these market data APIs are governed by each platform's official documentation.
Environment Information
| Environment | Base URL |
|---|---|
| Production | https://api-studio.r25.xyz |
1. Document Scope and Capability Overview
This document is intended for external partners integrating the OpenAPI query endpoints, Trading REST write endpoints, and Webhooks. It describes the endpoints available in the current version, the signature scheme, and the Webhook payloads. Hosts, Client IDs, and signatures in the examples are placeholders; replace them with values assigned by the platform or agreed by both parties.
Current capabilities include:
- Standard Vault: query a single Vault's metadata, the list of published Vaults, NAV history, and current asset positions; call the four ERC4626 preview operations.
- Rebate channel distribution: query cumulative commission and attribution flows.
- Transaction records: unified query of on-chain transaction events that have already been integrated, filtered by wallet address, Vault contract address, and other conditions.
- Trading REST: create orders, cancel orders, and close positions over HTTP;
HYPERLIQUIDandHABITTRADEare currently integrated. - Webhook: receive configured Vault and 7 types of Rebate change notifications;
eventTypeidentifies the business event.
2. Base URL, Content-Type, and Time / Number / Null Conventions
2.1 General Conventions
- Base URL: choose the corresponding address from the environment information at the beginning of this document.
- GET endpoints: all parameters are passed in the URL query string; no request body is sent.
- POST endpoints: the request body is JSON, using
Content-Type: application/json. - API responses: JSON, using the common response envelope defined in Section 4.
- URL query strings should be UTF-8 percent-encoded; for signing, the Canonical Query must additionally be generated as described in Section 3.
- This document only describes the APIs and Webhook callback protocol available to partners.
2.2 Time Conventions
- The
timestampin the unified response is a Unix timestamp in milliseconds. - Partners should use Unix seconds for the authentication header
X-Timestamp. Unix milliseconds andyyyy-MM-dd HH:mm:ssin the platform time zone are also accepted. - The
tsfield in the Trading REST request body is a Unix timestamp in milliseconds and must be close to the current server time; a request older than 10 seconds before the server's current time will be rejected. It is a separate field from the authentication headerX-Timestamp, and both must be provided. - The
datefield of Standard Vault NAV history is a millisecond timestamp string corresponding to 00:00 UTC of the given day. - The
requestBlockTimeandsettleBlockTimeof transaction records are in epoch seconds. - The Webhook header
X-Timestampis in Unix seconds. - The
occurredAtfield in a Webhook payload is an ISO 8601 offset time converted toAsia/Hong_Kong; the current offset is+08:00.
2.3 Number and Null Conventions
- On-chain amounts or quantities exposed as
Stringmust be handled as strings; do not convert them to a JavaScriptNumberor any type that may lose precision. - Decimal values are output as JSON numbers, e.g.
1.025,125000000, and should be parsed with an arbitrary-precision decimal type. - Unless otherwise stated, asset price fields are denominated in USD per asset unit; position weight, yield, and fee rate fields are expressed as percentages, e.g.
20means20%. - In Rebate commission responses, rate numbers represent percentages; asset quantity strings represent the raw on-chain asset amounts and are not subject to precision conversion, USD valuation, or cross-asset aggregation.
- All response fields documented here are included; fields with no available value are returned as
null.
3. Ed25519 V2 Authentication and Signing
3.1 Required Authentication Headers
All external endpoints in this document require the following headers:
| Header | Required | Description |
|---|---|---|
X-Client-Id | Yes | The unique partner identifier assigned by the platform and associated with the partner's Ed25519 public key. |
X-Timestamp | Yes | Request time. Unix seconds recommended; Unix milliseconds or yyyy-MM-dd HH:mm:ss are also accepted. |
X-Signature | Yes | The Ed25519 signature encoded as a hex string, exactly 128 hexadecimal characters, i.e. 64 bytes. |
Each X-Client-Id is associated with a 32-byte raw Ed25519 public key; the partner signs with the corresponding private key. Never expose the private key to the platform or to logs.
3.2 String to Sign
The string to sign is exactly:
METHOD\nPATH\nCANONICAL_QUERY\nTIMESTAMP\nSHA256_HEX(BODY)
Rules:
METHOD: the HTTP method in uppercase, e.g.GET,POST.PATH: the URL path only, excluding the host and query string, e.g./api/r25/standard-vault/vaultList.CANONICAL_QUERY: normalized as in the next section. When there is no query, use an empty string, but the corresponding empty line must be preserved.TIMESTAMP: must be exactly the same as the original text of theX-Timestamprequest header.SHA256_HEX(BODY): compute SHA-256 over the actually sent raw body UTF-8 bytes, then output lowercase hex. GET sends no body; the digest of empty bytes ise3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855.
For example:
GET
/api/r25/standard-vault/vaultList
pageNumber=1&pageSize=10
1786588800
e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855
Sign the complete UTF-8 string above with Ed25519, encode the 64-byte signature as a 128-character hex string, and put it in X-Signature.
3.3 Canonical Query
Canonical Query generation rules:
- Only the following keys are allowed:
pageNumber,pageSize,recordType,address,status,requestId,vaultAddress,shares,assets,startDate,endDate,channelCode,refcode,transactionType,eventTime. - Sort by key in ascending lexicographic order.
- Both the key and the value are percent-encoded per RFC 3986 in UTF-8;
A-Z a-z 0-9 - _ . ~are not encoded, spaces must be encoded as%20, and+must not be used. - Each entry is composed as
key=value, then joined with&. - Repeating the same key makes the request invalid.
- The query parameters sent in the URL must exactly match those used to build the Canonical Query, including their decoded values. Do not include parameters outside the allow list.
3.4 Time Window and Body Consistency
- The accepted server-side time skew is ±300 seconds around the current time.
yyyy-MM-dd HH:mm:ssis interpreted in the platform time zone. To avoid time-zone discrepancies, partners should always use Unix seconds.- A POST signature must be based on the final actually sent raw JSON bytes. Any change in field order, whitespace, line breaks, or escaping changes the body digest.
- On retry, regenerate the current timestamp and signature.
3.5 Java 17+ Complete Signing Example
The following example demonstrates how to read a standard PKCS#8 PEM private key safely held by the partner, generate the Canonical Query, build the five-part string to sign, perform the Ed25519 signature, and issue a GET request. Ed25519 is built into JDK 17; BouncyCastle is not required.
The query in the example must use only the keys allowed in Section 3.3 of this document. The code uses the same Canonical Query to build both the actual URL and the string to sign.
package client.sign;
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.security.KeyFactory;
import java.security.MessageDigest;
import java.security.PrivateKey;
import java.security.Signature;
import java.security.spec.PKCS8EncodedKeySpec;
import java.util.ArrayList;
import java.util.Base64;
import java.util.HexFormat;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.TreeMap;
public final class AkSkSigner {
private AkSkSigner() {
}
public static PrivateKey loadPrivateKeyFromPemFile(Path pemFile) throws Exception {
return loadPrivateKeyFromPem(Files.readString(pemFile, StandardCharsets.UTF_8));
}
public static PrivateKey loadPrivateKeyFromPem(String pem) throws Exception {
String base64 = pem
.replaceAll("-----BEGIN[^-]*-----", "")
.replaceAll("-----END[^-]*-----", "")
.replaceAll("\\s", "");
byte[] der = Base64.getDecoder().decode(base64);
return KeyFactory.getInstance("Ed25519")
.generatePrivate(new PKCS8EncodedKeySpec(der));
}
/** RFC3986: only A-Za-z0-9-_.~ are kept; other characters are converted to uppercase %XX per UTF-8 byte. */
static String rfc3986(String value) {
StringBuilder out = new StringBuilder();
for (byte b : value.getBytes(StandardCharsets.UTF_8)) {
int c = b & 0xff;
if ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z')
|| (c >= '0' && c <= '9') || c == '-' || c == '_'
|| c == '.' || c == '~') {
out.append((char) c);
} else {
out.append('%').append(String.format("%02X", c));
}
}
return out.toString();
}
/** Keys in ascending order, single value, joined as k=v with &; returns an empty string when there are no parameters. */
static String canonicalQuery(Map<String, String> params) {
if (params == null || params.isEmpty()) {
return "";
}
TreeMap<String, String> sorted = new TreeMap<>(params);
List<String> pairs = new ArrayList<>();
sorted.forEach((key, value) -> pairs.add(
rfc3986(key) + "=" + rfc3986(value == null ? "" : value)));
return String.join("&", pairs);
}
/** SHA-256 lowercase hex, fixed at 64 characters. */
static String sha256Hex(byte[] data) {
try {
MessageDigest digest = MessageDigest.getInstance("SHA-256");
return HexFormat.of().formatHex(digest.digest(data));
} catch (Exception e) {
throw new IllegalStateException("SHA-256 unavailable", e);
}
}
static String buildStringToSign(String method, String path, String canonicalQuery,
String timestamp, byte[] body) {
return method.toUpperCase(Locale.ROOT)
+ "\n" + path
+ "\n" + canonicalQuery
+ "\n" + timestamp
+ "\n" + sha256Hex(body);
}
/** Sign with the Ed25519 private key, returning 128 lowercase hex characters. */
static String signHex(PrivateKey privateKey, String stringToSign) throws Exception {
Signature signer = Signature.getInstance("Ed25519");
signer.initSign(privateKey);
signer.update(stringToSign.getBytes(StandardCharsets.UTF_8));
return HexFormat.of().formatHex(signer.sign());
}
public static HttpResponse<String> callSigned(
String baseUrl, String clientId, PrivateKey privateKey,
String method, String path,
Map<String, String> query, String jsonBody) throws Exception {
byte[] body = jsonBody == null
? new byte[0]
: jsonBody.getBytes(StandardCharsets.UTF_8);
String timestamp = String.valueOf(System.currentTimeMillis());
String canonicalQuery = canonicalQuery(query);
String stringToSign = buildStringToSign(
method, path, canonicalQuery, timestamp, body);
String signature = signHex(privateKey, stringToSign);
String url = baseUrl + path
+ (canonicalQuery.isEmpty() ? "" : "?" + canonicalQuery);
HttpRequest.Builder builder = HttpRequest.newBuilder(URI.create(url))
.header("X-Client-Id", clientId)
.header("X-Timestamp", timestamp)
.header("X-Signature", signature)
.header("Content-Type", "application/json");
builder = "GET".equalsIgnoreCase(method)
? builder.GET()
: builder.method(method.toUpperCase(Locale.ROOT),
HttpRequest.BodyPublishers.ofByteArray(body));
return HttpClient.newHttpClient().send(
builder.build(), HttpResponse.BodyHandlers.ofString());
}
public static void main(String[] args) throws Exception {
PrivateKey privateKey = loadPrivateKeyFromPemFile(
Path.of("/path/to/<client-id>-private-key.pem"));
String baseUrl = "https://<api-host>";
String clientId = "<client-id>";
// Example 1: GET + Query.
Map<String, String> query = new LinkedHashMap<>();
query.put("pageNumber", "1");
query.put("pageSize", "20");
HttpResponse<String> getResponse = callSigned(
baseUrl, clientId, privateKey,
"GET", "/api/r25/standard-vault/vaultList", query, null);
System.out.println("[GET] " + getResponse.statusCode()
+ " " + getResponse.body());
}
}
4. Common Response Envelope and External Business Error Codes
4.1 Response Envelope
Successful response:
{
"code": "200",
"message": "SUCCESS",
"timestamp": 1786588800123,
"data": {}
}
Failed response:
{
"code": "-1005",
"message": "pageSize must not exceed 100",
"timestamp": 1786588800123,
"data": {}
}
Field descriptions:
| Field | Type | Description |
|---|---|---|
code | String | Business response code; success is "200". Generic failures use the string form of a negative number; Trading REST business failures use uppercase enum strings. |
message | String | SUCCESS on success; an error explanation on failure, sometimes with the specific reason appended. |
timestamp | Long | The Unix timestamp in milliseconds at which the server generated the response. |
data | Any JSON value | Business data on success; null when the success data is empty. On failure it is usually an empty object {}; a Trading REST Vault permission failure returns deniedVaultAddresses. |
The response does not contain a success field. The HTTP status of both successful and failed business responses remains 200; partners must determine the business result from code in the JSON and must not rely on the HTTP status alone.
4.2 Current Business Error Codes
| code | Default message | Meaning |
|---|---|---|
-1001 | auth headers missing | Missing authentication headers; also used to reject duplicate query keys. The specific message explains the reason. |
-1002 | timestamp expired or invalid | The timestamp cannot be parsed or falls outside the ±300 second window. |
-1003 | invalid access key | The X-Client-Id is unknown, or no public key is currently available on the server. |
-1004 | signature mismatch | The Ed25519 signature does not match, or the signature hex format/length is incorrect. |
-1005 | form validation err | Query/JSON field validation failed, a required query parameter is missing, or a query key is not in the signature allow list. The specific message may describe the validation failure. |
-1006 | auth failed | Authentication failed. |
-1007 | too many requests | The request rate limit was triggered. |
-1008 | vault not permitted | The current partner identity is not permitted to access the specified Vault. |
-1017 | request replayed | An identical signed request was resent within the authentication time window. |
-1010 | query metadata failed | The metadata query failed; a Standard Vault metadata request with a missing or unknown address also uses this code. |
-1012 | query nav history failed | The Standard Vault NAV history query failed, or the returned data does not conform to the contract. |
-1019 | query channel distribution failed | The Rebate query failed; when a valid business error message is available, that message takes precedence. |
-1020 | preview contract call failed | The preview calculation failed. |
-1021 | query positions failed | The Standard Vault current positions query failed or the Vault address is empty. |
-1022 | query vault events failed | The Vault events query failed. |
-1023 | vault configuration failed | The Vault configuration update failed, the Vault address could not be mapped uniquely, or the downstream response violated the contract. |
-5000 | server error | An unexpected service error occurred. |
When a Rebate query returns a recognizable business error, the response uses the corresponding code above and includes the business error message in message when one is available. Otherwise, the default message in the table is used. Network timeouts and service errors return -5000 with server error.
After a Trading REST request passes common authentication, JSON parsing, and rate limiting, business failures use the following string code:
| code | Meaning | Retry advice |
|---|---|---|
INVALID_REQUEST | The request envelope, required fields, enums, values, or ranges are invalid. | Correct the request and retry. |
VAULT_NOT_PERMITTED | The current X-Client-Id has no permission to operate on the vaultAddress, or the address cannot be resolved to a Vault. | Do not retry; verify the authorization relationship. |
INVALID_EXCHANGE | exchange is missing or is not HYPERLIQUID / HABITTRADE. | Correct the request and retry. |
FEATURE_UNSUPPORTED | The current exchange does not support the operation, e.g. HabitTrade order cancellation, or Hyperliquid requests that explicitly specify closeMode=PARTIAL. | Do not retry with the same parameters. |
INVALID_PRICE_TICK | The price does not conform to the current exchange's price precision configuration. | Correct to a valid price precision and retry. |
INVALID_SIZE_STEP | The quantity does not conform to the current exchange's quantity precision configuration. | Correct to a valid quantity precision and retry. |
EXCHANGE_REJECTED | The exchange returned a business rejection or failure result. | Correct according to the message; do not retry unconditionally. |
UPSTREAM_RESULT_UNKNOWN | A write request timed out or errored; the server cannot confirm whether the exchange accepted it. | Do NOT retry directly; query the order or position first to confirm the result. |
INTERNAL_ERROR | The request could not be processed because of an unexpected service error. | Retain the request information and contact the platform for investigation. |
4.3 Request Rate Limits
The Standard Vault, Preview, Rebate channel distribution, transaction records, and Trading REST endpoints are rate-limited along the following two dimensions independently:
| Limit dimension | Allowed request frequency per endpoint |
|---|---|
| IP | Up to 60 requests per 60 seconds |
X-Client-Id | Up to 120 requests per 1 hour |
- The limit is counted per endpoint independently; request counts are not shared across endpoints.
- When either the IP or the
X-Client-Iddimension reaches the limit, the current request is rejected. - When the limit is exceeded, the HTTP status remains
200and the business response code is-1007with the default messagetoo many requests. - Rate-limit responses return a suggested wait time via the
Retry-Afterheader, in seconds and rounded up; partners should retry after that time.
5. Endpoint Overview
| Category | Method | Path | Request location | Current status |
|---|---|---|---|---|
| Standard Vault | GET | /api/r25/standard-vault/metadata | Query | Available; vaultAddress is actually required |
| Standard Vault | GET | /api/r25/standard-vault/vaultList | Query (pageNumber, pageSize) | Available |
| Standard Vault | GET | /api/r25/standard-vault/events | Query (eventTime, pageNumber, pageSize) | Available |
| Standard Vault | GET | /api/r25/standard-vault/nav/history | Query | Available |
| Standard Vault | GET | /api/r25/standard-vault/positions | Query | Available; vaultAddress required |
| Standard Vault | POST | /api/r25/standard-vault/configuration | JSON Body | Available |
| Standard Vault Preview | GET | /api/r25/standard-vault/preview/mint | Query | Available |
| Standard Vault Preview | GET | /api/r25/standard-vault/preview/redeem | Query | Available |
| Standard Vault Preview | GET | /api/r25/standard-vault/preview/deposit | Query | Available |
| Standard Vault Preview | GET | /api/r25/standard-vault/preview/withdraw | Query | Available |
| Rebate | GET | /api/r25/channel-distribution/commission | Query | Available |
| Rebate | GET | /api/r25/channel-distribution/transactions | Query | Available |
| Rebate | GET | /api/r25/channel-distribution/merkle-proofs/batch | Query | Available |
| Transaction records | GET | /api/r25/standard-vault/transaction/records | Query | Unified transaction records entry |
| Trading REST | POST | /api/r25/trade/order/create | JSON Body | Available; supports Hyperliquid / HabitTrade |
| Trading REST | POST | /api/r25/trade/order/cancel | JSON Body | Hyperliquid only |
| Trading REST | POST | /api/r25/trade/position/close | JSON Body | Available; rules differ by exchange |
6. Standard Vault Endpoints
6.1 Common Metadata Response Structure
Standard Vault Metadata returns the layered VaultMetadataResponse structure. The response is grouped into basic info, dynamic metrics, transaction configuration, asset configuration, access control, contract configuration, risk configuration, and external integration configuration.
All documented fields are included in the response. Except where stated otherwise, a field is null when its value is unavailable or not applicable to that Vault type; fields are not automatically filled with "0", false, an empty string, or a zero address. An unavailable array may be null; an available array with no elements is [].
Top-level fields:
| Field | Type | Description |
|---|---|---|
vaultBasicInfo | Object | Vault basic information. |
vaultMetrics | Object | Vault current dynamic metrics. |
transactionConfigInfo | Object | Vault subscription/redemption, limits, settlement, and fee configuration. |
assetConfig | Object | Share token and underlying asset configuration. |
accessControl | Object | Curator and subscription whitelist configuration. |
contractConfig | Object | Core contract and module contract configuration. |
riskConfig | Object | Vault risk configuration. |
prosperInfo | Object | Prosper external integration configuration. |
vaultBasicInfo fields:
| Field | Type | Description |
|---|---|---|
vaultName | String | Vault name; null when there is no data. |
vaultLogoUrl | String | Vault logo URL; null when there is no data. |
curationStrategyDescription | String | Strategy description configured by the Curator; null when there is no data. |
chain | Object | Chain information of the Vault; fields are listed in the table below. |
offline | Boolean | Whether the Vault is offline; null when there is no data. |
deployTime | String | Contract deployment time in the format yyyy-MM-dd'T'HH:mm:ss with no time-zone offset. |
vaultBasicInfo.chain fields:
| Field | Type | Description |
|---|---|---|
chainId | String | Chain ID. |
chainName | String | Chain name. |
vaultMetrics fields:
| Field | Type | Description |
|---|---|---|
tvl | String | Total value locked, represented as a decimal string. |
cFunding | String | The raw on-chain amount of cumulative net USDC subscriptions by the fund manager; returns "0" when the Curator address or aggregation result is missing. |
apy | String | Annualized yield in percent; e.g. "8.25" means 8.25%. |
nav | String | Current unit NAV, returned as a plain decimal string with insignificant trailing zeros removed. |
currentShareHolders | String | Current number of share holder addresses. |
shareTokenTotalSupply | String | Current share token total supply; for Standard Vaults it is already converted to the Share Token precision. |
transactionConfigInfo fields:
| Field | Type | Description |
|---|---|---|
flexibleSubAndRedeem | Boolean | Whether flexible subscription and redemption is supported. When the value is true, all three windows return null, and both depositAvailable and redeemAvailable are true. |
cutOffTime | String | Settlement time. |
depositToken | Object | Subscription and redemption token name, address, and decimals; returns null only when all three fields are null. |
subscriptionWindow | Object | Subscription window; null when flexible subscription/redemption applies or there is no data. |
redemptionWindow | Object | Redemption window; null when flexible subscription/redemption applies or there is no data. |
lockWindow | Object | Product lock window; null when flexible subscription/redemption applies or there is no data. |
limits | Object | Subscription/redemption and overall Vault limits. |
settlement | Object | Subscription/redemption settlement configuration. |
feeRates | Object | Vault fee rate configuration. |
depositAvailable | Boolean | Whether subscription is currently allowed. For a non-flexible Standard Vault it is computed from whether the current time falls within the subscription window; returns null when the window or either boundary is missing. |
redeemAvailable | Boolean | Whether redemption is currently allowed. For a non-flexible Standard Vault it is computed from whether the current time falls within the redemption window; returns null when the window or either boundary is missing. |
transactionConfigInfo.depositToken fields:
| Field | Type | Description |
|---|---|---|
tokenName | String | Configured subscription and redemption token name; null when missing. |
address | String | Subscription and redemption token contract address; null when no matching data exists. |
decimals | String | Subscription and redemption token decimals; null when no matching data exists. |
Common fields of subscriptionWindow, redemptionWindow, and lockWindow:
| Field | Type | Description |
|---|---|---|
startDate | String | Window start time in the format yyyy-MM-dd'T'HH:mm:ss with no time-zone offset. |
endDate | String | Window end time in the format yyyy-MM-dd'T'HH:mm:ss with no time-zone offset. |
periodDays | String | Window duration in days, returned as a string. |
transactionConfigInfo.limits fields:
| Field | Type | Description |
|---|---|---|
deposit | Object | Per-order subscription amount limits. |
deposit.minAmount | String | Minimum subscription amount per order. USD |
deposit.maxAmount | String | Maximum subscription amount per order. USD |
redemption | Object | Per-order redemption limits. |
redemption.minShares | String | Minimum redemption share quantity per order. Standard Vaults currently return "0" to indicate no limit. share |
redemption.maxShares | String | Maximum redemption share quantity per order. Standard Vaults currently return "0" to indicate no limit. share |
redemption.minAssets | String | Minimum redemption asset quantity per order. Standard Vaults currently return "0" to indicate no limit. share |
vault | Object | Overall Vault limits. |
vault.maxCap | String | Maximum share supply of the Vault; not a fixed value. |
vault.maxTvl | String | Maximum TVL of the Vault. Standard Vaults currently return "0" to indicate no limit. USD |
Standard Vault
"0"special semantics: Currently the Standard Metadata fieldsredemption.minShares,redemption.maxShares,redemption.minAssets, andvault.maxTvlalways return"0". In these four fields,"0"explicitly means no limit, not a zero quota, and does not indicate that subscription or redemption is forbidden. This rule applies only to these four fixed Standard fields; a"0"in any other field must not be inferred to mean no limit.
transactionConfigInfo.settlement fields:
| Field | Type | Description |
|---|---|---|
settleType | String | Settlement type: "0" means synchronous, "1" means asynchronous. |
depositSettlement | String | Subscription settlement cycle, returned as a numeric string such as "0", "1"; no T+ prefix is added. |
redemptionSettlement | String | Redemption settlement cycle, returned as a numeric string such as "0", "1"; no T+ prefix is added. |
transactionConfigInfo.feeRates fields:
| Field | Type | Description |
|---|---|---|
depositFeeRate | String | User subscription fee rate, in percent. |
withdrawalFeeRate | String | User withdrawal fee rate, in percent. |
performanceFeeRate | String | Vault performance fee rate, in percent. |
managementFeeRate | String | Vault management fee rate, in percent. |
assetConfig fields:
| Field | Type | Description |
|---|---|---|
shareToken | Object | Static information of the share token. |
underlyingAssets | Array | Underlying asset list; element fields are listed in the table below. |
assetConfig.shareToken fields:
| Field | Type | Description |
|---|---|---|
address | String | Share token contract address. |
addressAtBlock | String | Share token deployment block height, returned as a string. |
decimals | String | Share token decimals, returned as a string. |
symbol | String | Share token symbol. |
name | String | Share token name. |
assetConfig.underlyingAssets[] fields:
| Field | Type | Description |
|---|---|---|
targetVaultAddress | String | Target Vault address associated with the underlying asset. |
vaultToken | Object | Underlying asset token information. |
vaultToken.address | String | Underlying asset token contract address. |
vaultToken.name | String | Underlying asset token name. |
vaultToken.symbol | String | Underlying asset token symbol. |
vaultToken.decimals | String | Underlying asset token decimals, returned as a string. |
adapter | Object | Underlying asset Adapter information. |
adapter.id | String | Adapter identifier. |
adapter.type | String | Adapter type. |
percentageBps | String | Initial asset allocation ratio, returned as a string without ratio conversion. |
logoUrl | String | Underlying asset logo URL. |
accessControl fields:
| Field | Type | Description |
|---|---|---|
curatorAddress | String | Curator permission address. |
subscriptionWhitelist | Object | Subscription whitelist configuration. |
subscriptionWhitelist.enabled | Boolean | Whether the subscription whitelist restriction is enabled. |
subscriptionWhitelist.contract | Object | Subscription whitelist guard contract deployment information. |
contractConfig fields:
| Field | Type | Description |
|---|---|---|
coreContract | Object | Vault core contract deployment information. |
moduleContracts | Object | Vault module contract deployment information. |
Common fields of coreContract, subscriptionWhitelist.contract, and each module contract:
| Field | Type | Description |
|---|---|---|
address | String | On-chain contract address. |
addressAtBlock | String | Contract deployment block height, returned as a string. |
contractConfig.moduleContracts fields:
| Field | Type | Description |
|---|---|---|
feeRouterContract | Object | Fee router contract. |
entryFixedBpsFeeContract | Object | Subscription fixed-bps fee contract. |
exitFixedBpsFeeContract | Object | Redemption fixed-bps fee contract. |
timeAccrualFeeContract | Object | Time-accrual fee contract. |
hwmPerformanceFeeContract | Object | High-water-mark performance fee contract. |
commissionContract | Object | Project commission collection contract. |
navContract | Object | NAV query contract. |
riskConfig fields:
| Field | Type | Description |
|---|---|---|
riskLevel | String | Vault risk level. |
strategyRiskDisclosureText | String | Strategy risk disclosure text. |
riskDisclosure | String | Vault risk disclosure body text. |
prosperInfo fields:
| Field | Type | Description |
|---|---|---|
prosperTokenName | String | Prosper token name identifier. |
reward | String | Target Reward contract address. |
validProsper | String | Whether to include the Pharos token: "1" means included, "0" means not included. |
6.2 Query a Single Standard Vault Metadata
Purpose
Query the metadata of a single published Standard Vault by its Vault contract address.
Method + Path
GET /api/r25/standard-vault/metadata
Request location
Query; no request body.
Request fields
| Field | Type | Required | Default/Enum | Description |
|---|---|---|---|---|
vaultAddress | String | Yes | None | Vault contract address; leading and trailing whitespace is trimmed before querying. |
Request example
curl --get 'https://<api-host>/api/r25/standard-vault/metadata' \
--data-urlencode 'vaultAddress=0x1111111111111111111111111111111111111111' \
-H 'X-Client-Id: <client-id>' \
-H 'X-Timestamp: <unix-seconds>' \
-H 'X-Signature: <128-char-ed25519-signature-hex>'
data** response field description**
data uses the complete layered Metadata structure in Section 6.1. A Standard Vault returns the currently available basic info, dynamic metrics, transaction configuration, asset configuration, access control, contract configuration, risk configuration, and Prosper info; fields that are currently unavailable or not applicable return null.
Response example
{
"code": "200",
"message": "SUCCESS",
"timestamp": 1786588800123,
"data": {
"vaultBasicInfo": {
"vaultName": "Example USDC Yield Vault",
"vaultLogoUrl": "https://static.example.com/vault.png",
"curationStrategyDescription": "Example strategy description",
"chain": {
"chainId": "1",
"chainName": "Ethereum"
},
"offline": false,
"deployTime": "2026-07-29T14:43:52"
},
"vaultMetrics": {
"tvl": "125000000",
"cFunding": "1000000",
"apy": "8.25",
"nav": "1.025",
"currentShareHolders": "128",
"shareTokenTotalSupply": "125"
},
"transactionConfigInfo": {
"flexibleSubAndRedeem": false,
"cutOffTime": "16:00:00",
"depositToken": {
"tokenName": "USDC",
"address": "0xc879c018db60520f4355c26ed1a6d572cdac1815",
"decimals": "6"
},
"subscriptionWindow": {
"startDate": "2026-08-05T00:00:00",
"endDate": "2026-08-06T00:00:00",
"periodDays": "1"
},
"redemptionWindow": {
"startDate": "2026-08-07T00:00:00",
"endDate": "2026-08-14T00:00:00",
"periodDays": "7"
},
"lockWindow": {
"startDate": "2026-08-06T00:00:00",
"endDate": "2026-08-07T00:00:00",
"periodDays": "1"
},
"limits": {
"deposit": {
"minAmount": "1000000",
"maxAmount": "100000000"
},
"redemption": {
"minShares": "0",
"maxShares": "0",
"minAssets": "0"
},
"vault": {
"maxCap": "125000000",
"maxTvl": "0"
}
},
"settlement": {
"settleType": "1",
"depositSettlement": "0",
"redemptionSettlement": "1"
},
"feeRates": {
"depositFeeRate": "0",
"withdrawalFeeRate": "0.1",
"performanceFeeRate": "20",
"managementFeeRate": "2"
},
"depositAvailable": true,
"redeemAvailable": false
},
"assetConfig": {
"shareToken": {
"address": "0x2222222222222222222222222222222222222222",
"addressAtBlock": "23456780",
"decimals": "18",
"symbol": "vUSDC",
"name": null
},
"underlyingAssets": [
{
"targetVaultAddress": "0x4444444444444444444444444444444444444444",
"vaultToken": {
"address": "0x3333333333333333333333333333333333333333",
"name": "USD Yield Token",
"symbol": "USDY",
"decimals": "18"
},
"adapter": {
"id": "0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
"type": "erc4626"
},
"percentageBps": "100",
"logoUrl": "https://static.example.com/usdy.png"
}
]
},
"accessControl": {
"curatorAddress": "0x4444444444444444444444444444444444444444",
"subscriptionWhitelist": {
"enabled": true,
"contract": {
"address": "0xb7d5c363f4bb438d484b64d4fba766bb2b9d9238",
"addressAtBlock": "23456780"
}
}
},
"contractConfig": {
"coreContract": {
"address": "0x1111111111111111111111111111111111111111",
"addressAtBlock": "23456780"
},
"moduleContracts": {
"feeRouterContract": {
"address": "0x5555555555555555555555555555555555555555",
"addressAtBlock": "23456780"
},
"entryFixedBpsFeeContract": {
"address": "0x6666666666666666666666666666666666666666",
"addressAtBlock": "23456780"
},
"exitFixedBpsFeeContract": {
"address": "0x7777777777777777777777777777777777777777",
"addressAtBlock": "23456780"
},
"timeAccrualFeeContract": {
"address": "0x8888888888888888888888888888888888888888",
"addressAtBlock": "23456780"
},
"hwmPerformanceFeeContract": {
"address": "0x9999999999999999999999999999999999999999",
"addressAtBlock": "23456780"
},
"commissionContract": {
"address": "0xbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb",
"addressAtBlock": "23456780"
},
"navContract": {
"address": "0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
"addressAtBlock": "23456780"
}
}
},
"riskConfig": {
"riskLevel": "MEDIUM",
"strategyRiskDisclosureText": null,
"riskDisclosure": null
},
"prosperInfo": {
"prosperTokenName": "pUSDY",
"reward": "0xcccccccccccccccccccccccccccccccccccccccc",
"validProsper": "1"
}
}
}
In the example above, the three
"0"values intransactionConfigInfo.limits.redemptionand the"0"intransactionConfigInfo.limits.vault.maxTvlall mean no limit, not a zero quota. This interpretation applies only to these four fixed Standard fields and must not be extended to other fields whose value is"0".
Specific limits
- A missing, blank, or unknown
vaultAddressreturns-1010. EVM address format is not currently validated; partners should still pass a standard address. - Only published and enabled Vaults can be queried.
6.3 Query the Standard Vault List
Purpose
Query the contract addresses and creation times of published Vaults available to the current partner.
Method + Path
GET /api/r25/standard-vault/vaultList
Request location
Query; no request body.
Request fields
| Field | Type | Required | Default/Enum | Description |
|---|---|---|---|---|
pageNumber | Integer | No | 1 | Page number, must be ≥ 1. |
pageSize | Integer | No | 10 | Page size, in the range 1 to 100. |
Request example
curl 'https://<api-host>/api/r25/standard-vault/vaultList?pageNumber=1&pageSize=10' \
-H 'X-Client-Id: <client-id>' \
-H 'X-Timestamp: <unix-seconds>' \
-H 'X-Signature: <128-char-ed25519-signature-hex>'
data** response field description**
| Field | Type | Description |
|---|---|---|
list | Object[] | Vault info in the current page; [] when there is no data. |
list[].vaultAddress | String | Vault contract address. |
list[].createTime | Long | Vault creation time, Unix millisecond timestamp. |
pageNumber | Integer | Current page number. |
pageSize | Integer | Page size. |
hasNext | Boolean | Whether a next page exists. |
The list is returned in a stable order.
Response example
{
"code": "200",
"message": "SUCCESS",
"timestamp": 1786588800123,
"data": {
"list": [
{
"vaultAddress": "0x1111111111111111111111111111111111111111",
"createTime": 1785920900000
},
{
"vaultAddress": "0x2222222222222222222222222222222222222222",
"createTime": 1785982530000
}
],
"pageNumber": 1,
"pageSize": 10,
"hasNext": false
}
}
Specific limits
- Only published and enabled Vaults are returned.
- Each element in
listis an object containingvaultAddressandcreateTime. - Request validation fails when
pageNumberis less than1orpageSizeis outside the1–100range.
6.4 Query Vault Events
Purpose
Starting from the given time, query the Vault events integrated by the system in ascending event time order with pagination. The total count is not returned.
Method + Path
GET /api/r25/standard-vault/events
Request location
Query; no request body.
Request fields
| Field | Type | Required | Default/Format | Description |
|---|---|---|---|---|
eventTime | String | Yes | yyyy-MM-dd HH:mm:ss | Query start time, inclusive; interpreted in Asia/Hong_Kong (UTC+8). |
pageNumber | Integer | No | 1 | Page number, must be ≥ 1. |
pageSize | Integer | No | 10 | Page size, in the range 1 to 100. |
Request example
curl --get 'https://<api-host>/api/r25/standard-vault/events' \
--data-urlencode 'eventTime=2026-08-24 16:00:00' \
--data-urlencode 'pageNumber=1' \
--data-urlencode 'pageSize=10' \
-H 'X-Client-Id: <client-id>' \
-H 'X-Timestamp: <unix-seconds>' \
-H 'X-Signature: <128-char-ed25519-signature-hex>'
data** response field description**
| Field | Type | Description |
|---|---|---|
list | Object[] | Vault events in the current page; [] when there is no data. |
list[].eventType | String | Vault lifecycle event type: DEPLOYED, GRAY, or OFFICIAL. |
list[].coreVaultAddress | String | Core Vault contract address. |
list[].changeType | String | Change type. |
list[].changeTime | Long | Change time, Unix millisecond timestamp. |
list[].transactionHash | String | Transaction hash of the change. |
list[].blockHeight | Long | Block height of the change. |
list[].vaultStatus | String | Vault status after the change. |
pageNumber | Integer | Current page number. |
pageSize | Integer | Page size. |
hasNext | Boolean | Whether a next page exists. |
list[].eventType and list[].vaultStatus describe different concepts: a GRAY event has vaultStatus=DEPLOYED; an OFFICIAL event has vaultStatus=OFFICIAL.
Response example
{
"code": "200",
"message": "SUCCESS",
"timestamp": 1786588800123,
"data": {
"list": [
{
"eventType": "DEPLOYED",
"coreVaultAddress": "0x1111111111111111111111111111111111111111",
"changeType": "FINALIZE_PRODUCT",
"changeTime": 1786588800000,
"transactionHash": "0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
"blockHeight": 12345678,
"vaultStatus": "DEPLOYED"
}
],
"pageNumber": 1,
"pageSize": 10,
"hasNext": false
}
}
Specific limits
- The list is returned in ascending change-time order in a stable manner; the start time is inclusive.
- Request validation fails when
eventTimeis missing or does not matchyyyy-MM-dd HH:mm:ss, whenpageNumberis less than1, or whenpageSizeis outside the1–100range. - Returns
-1022when the query fails. - The endpoint returns the actual Vault lifecycle type of each event in
eventType. - Nullable fields in a list item return
nullwhen no value is currently available.
6.5 Query Standard Vault NAV History
Purpose
Query the daily NAV history by Vault contract address, optionally filtered by date range.
Method + Path
GET /api/r25/standard-vault/nav/history
Request location
Query; no request body.
Request fields
| Field | Type | Required | Default/Format | Description |
|---|---|---|---|---|
vaultAddress | String | Yes | Vault contract address | Used to locate the Standard Vault. |
startDate | String | No | yyyy-MM-dd | Start date for the NAV query. |
endDate | String | No | yyyy-MM-dd | End date for the NAV query. |
Request example
curl --get 'https://<api-host>/api/r25/standard-vault/nav/history' \
--data-urlencode 'vaultAddress=0x1111111111111111111111111111111111111111' \
--data-urlencode 'startDate=2026-08-01' \
--data-urlencode 'endDate=2026-08-07' \
-H 'X-Client-Id: <client-id>' \
-H 'X-Timestamp: <unix-seconds>' \
-H 'X-Signature: <128-char-ed25519-signature-hex>'
data** response field description**
| Field | Type | Description |
|---|---|---|
list | Array | Daily NAV records; [] when there is no data. |
list[].date | String | Millisecond timestamp string corresponding to 00:00 UTC of the day. |
list[].nav | Number | Unit NAV of the day, i.e. the base asset quantity per 1 share. |
Response example
{
"code": "200",
"message": "SUCCESS",
"timestamp": 1786588800123,
"data": {
"list": [
{
"date": "1785974400000",
"nav": 1.025
}
]
}
}
Specific limits
- Returns
-1012whenvaultAddressis missing or empty, or when the NAV query fails. - Only
dateandnavare returned.
6.6 Query Standard Vault Current Positions
Purpose
Query current underlying asset positions by Vault contract address. The response includes the asset symbol, contract address, USD price, position weight, logo URL, and 24-hour price change percentage.
Method + Path
GET /api/r25/standard-vault/positions
Request location
Query; no request body.
Request fields
| Field | Type | Required | Default/Format | Description |
|---|---|---|---|---|
vaultAddress | String | Yes | None | Vault contract address. EVM address format is not currently validated. |
Request example
curl --get 'https://<api-host>/api/r25/standard-vault/positions' \
--data-urlencode 'vaultAddress=0x1111111111111111111111111111111111111111' \
-H 'X-Client-Id: <client-id>' \
-H 'X-Timestamp: <unix-seconds>' \
-H 'X-Signature: <128-char-ed25519-signature-hex>'
data** response field description**
data is an array of positions; when there are no positions, it is an empty array [].
| Field | Type | Description |
|---|---|---|
[].symbol | String | Asset symbol. |
[].address | String | Asset contract address. |
[].price | Number | Current price per asset unit in USD. |
[].weight | Number | Current position weight in percent; e.g. 25.5 means 25.5%. |
[].logoUrl | String | Asset logo URL. |
[].priceChange | Number | Price change percentage over the past 24 hours. A positive value indicates an increase and a negative value indicates a decrease; e.g. -0.0060 means -0.0060%. |
Response example
{
"code": "200",
"message": "SUCCESS",
"timestamp": 1786588800123,
"data": [
{
"symbol": "USDC",
"address": "0x3333333333333333333333333333333333333333",
"price": 1.0000,
"weight": 40.0000,
"logoUrl": "https://static.example.com/usdc.png",
"priceChange": -0.0060
},
{
"symbol": "USDY",
"address": "0x4444444444444444444444444444444444444444",
"price": 1.0250,
"weight": 60.0000,
"logoUrl": "https://static.example.com/usdy.png",
"priceChange": 0.9300
}
]
}
Specific limits
- Returns
-1021whenvaultAddressis missing or blank, or when the positions query fails. - When
symbol,address,price,weight,logoUrl, orpriceChangehas no value, the corresponding field returnsnull. - The endpoint returns the positions list and does not include total AUM, asset quantity, holding quantity, or holding value.
6.7 Standard Vault Preview
Purpose
Perform ERC4626 read-only previews via the Lens contract to convert between asset quantity and share quantity without sending on-chain transactions.
Method + Path
| Method | Path | Input | Contract preview result |
|---|---|---|---|
| GET | /api/r25/standard-vault/preview/mint | shares | assets required to deposit |
| GET | /api/r25/standard-vault/preview/redeem | shares | assets redeemable |
| GET | /api/r25/standard-vault/preview/deposit | assets | shares mintable |
| GET | /api/r25/standard-vault/preview/withdraw | assets | shares to burn |
Request location
Query; no request body.
Request fields
| Field | Type | Required | Applicable endpoints | Description |
|---|---|---|---|---|
vaultAddress | String | Yes | All | 0x or 0X prefix plus 40 hexadecimal characters. |
shares | String | Yes | mint, redeem | On-chain minimum-unit quantity of shares, as a uint256 decimal string; only 0 or a non-negative integer not starting with 0 is allowed. |
assets | String | Yes | deposit, withdraw | On-chain minimum-unit quantity of the underlying asset, as a uint256 decimal string; only 0 or a non-negative integer not starting with 0 is allowed. |
Request example
curl --get 'https://<api-host>/api/r25/standard-vault/preview/mint' \
--data-urlencode 'vaultAddress=0x1111111111111111111111111111111111111111' \
--data-urlencode 'shares=1000000000000000000' \
-H 'X-Client-Id: <client-id>' \
-H 'X-Timestamp: <unix-seconds>' \
-H 'X-Signature: <128-char-ed25519-signature-hex>'
curl --get 'https://<api-host>/api/r25/standard-vault/preview/deposit' \
--data-urlencode 'vaultAddress=0x1111111111111111111111111111111111111111' \
--data-urlencode 'assets=1000000' \
-H 'X-Client-Id: <client-id>' \
-H 'X-Timestamp: <unix-seconds>' \
-H 'X-Signature: <128-char-ed25519-signature-hex>'
data** response field description**
| Field | Type | Description |
|---|---|---|
vaultAddress | String | Echoes the Vault contract address from the request. |
shares | String | On-chain minimum-unit quantity of shares; echoed from the input in mint/redeem, and the contract result in deposit/withdraw. |
assets | String | On-chain minimum-unit quantity of the underlying asset; echoed from the input in deposit/withdraw, and the contract result in mint/redeem. |
Response example
{
"code": "200",
"message": "SUCCESS",
"timestamp": 1786588800123,
"data": {
"vaultAddress": "0x1111111111111111111111111111111111111111",
"shares": "1000000000000000000",
"assets": "1025000"
}
}
Specific limits
shares/assetsmust not exceed2^256 - 1; an invalid format or value range returns-1005.- Returns
-1020when the preview calculation fails.
6.8 Vault Page Configuration
Purpose
After completing canary or official publication processing for a Vault, the partner uses this endpoint to report the access link and processing status.
When eventType=GRAY, dappLink is the canary access link. When eventType=OFFICIAL, dappLink is the official access link.
Method + Path
POST /api/r25/standard-vault/configuration
Request location
JSON Body; Content-Type: application/json. The body SHA-256 for signing must be computed from the final sent raw JSON bytes.
Request fields
| Field | Type | Required | Format/Enum | Description |
|---|---|---|---|---|
dappLink | String | No | Max 2048 characters | Access link. It is the canary link when eventType=GRAY and the official link when eventType=OFFICIAL. Omit it when no link is available. |
eventType | String | Yes | GRAY, OFFICIAL | Publication stage associated with this report; values are case-sensitive. |
status | String | Yes | See the table below | Processing result; values are case-sensitive. |
vaultAddress | String | Yes | 0x or 0X + 40 hexadecimal characters | Vault on-chain contract address. |
remark | String | No | No additional format restriction | Status details, such as a delisting or rejection reason. |
Status values
status | Meaning | When used |
|---|---|---|
GRAY | Canary published | The canary link has been generated and the Curator can access the Vault through it. |
OFFICIAL | Officially published | The Vault is displayed in the Dapp Vaults list. |
DELISTED | Delisted | The Vault has been removed from the Dapp. |
REJECTED | Rejected | The Vault did not pass the Dapp-side review. |
OTHER | Other | An exceptional situation that cannot be classified under the statuses above. |
Canary publication request example
curl 'https://<api-host>/api/r25/standard-vault/configuration' \
-X POST \
-H 'Content-Type: application/json' \
-H 'X-Client-Id: <client-id>' \
-H 'X-Timestamp: <unix-seconds>' \
-H 'X-Signature: <128-char-ed25519-signature-hex>' \
--data-raw '{"dappLink":"https://gray.example.com/vault","eventType":"GRAY","status":"GRAY","vaultAddress":"0x1111111111111111111111111111111111111111","remark":"Canary review approved"}'
Official publication request example
{
"dappLink": "https://dapp.example.com/vault",
"eventType": "OFFICIAL",
"status": "OFFICIAL",
"vaultAddress": "0x1111111111111111111111111111111111111111",
"remark": "Official publication completed"
}
Success response
data is the configuration result returned by the Vault service. A partner may treat the configuration update as successful only when data=true.
{
"code": "200",
"message": "SUCCESS",
"timestamp": 1788480000000,
"data": true
}
Failure semantics
- Returns
-1005when the request body,eventType,status, orvaultAddressfails validation. - Returns
-1008when the current partner is not permitted to operate the specified Vault. - Returns
-1023when the Vault address cannot be mapped uniquely, or when the Vault service returns a failed or invalid response. - Returns
-5000for Vault service network errors, timeouts, or other unexpected failures.
7. Rebate Channel Distribution Endpoints
7.1 Query Channel Cumulative Commission
Purpose
Query the current distribution rules of a given channel and Vault, and the cumulative commission aggregated by Refcode.
Method + Path
GET /api/r25/channel-distribution/commission
Request location
Query; no request body.
Request fields
| Field | Type | Required | Default/Enum | Description |
|---|---|---|---|---|
channelCode | String | No | None | Optional channel stable code; must not be blank-only when provided; leading and trailing whitespace is trimmed before use. |
refcode | String | No | None | Refcode; must not be blank-only when provided; leading and trailing whitespace is trimmed before use. |
vaultAddress | String | No | 0x + 40 hexadecimal characters | Optional Core Vault contract address; must conform to the EVM address format when provided, and lowercased before querying. |
Request example
curl --get 'https://<api-host>/api/r25/channel-distribution/commission' \
--data-urlencode 'channelCode=CHANNEL_EXAMPLE' \
--data-urlencode 'refcode=REF_EXAMPLE' \
--data-urlencode 'vaultAddress=0x1111111111111111111111111111111111111111' \
-H 'X-Client-Id: <client-id>' \
-H 'X-Timestamp: <unix-seconds>' \
-H 'X-Signature: <128-char-ed25519-signature-hex>'
data** response field description**
| Field | Type | Description |
|---|---|---|
dataAsOf | String | Data statistic time point, ISO 8601 UTC. |
vault.chainId | Long | Chain ID. |
vault.vaultAddress | String | Core Vault contract address. |
vault.name | String | Vault name. |
vault.symbol | String | Vault share token symbol. |
vault.assetAddress | String | Base asset address. |
vault.assetDecimals | Integer | Base asset decimals. |
channel.channelCode | String | Channel code. |
channel.name | String | Channel name. |
channel.status | String | Channel status. |
channel.payoutWallet.address | String | Channel commission payout wallet address. |
channel.payoutWallet.chainCode | String | Payout wallet chain identifier. |
items | Array | Refcode-dimension distribution and commission results. |
items[].refcode.code | String | Refcode. |
items[].refcode.label | String | Refcode label. |
items[].refcode.status | String | Refcode status. |
items[].distribution.enabled | Boolean | Whether distribution is currently enabled. |
items[].distribution.status | String | Distribution status. |
items[].distribution.effectiveRates.managementRate | Number | Current management fee rebate percentage. |
items[].distribution.effectiveRates.performanceRate | Number | Current performance fee rebate percentage. |
items[].distribution.rateSource | String | Source of the currently effective rates. |
items[].distribution.effectivePeriod.startTime | String | Start time of the current rule. |
items[].distribution.effectivePeriod.endTime | String | End time of the current rule; null when there is none. |
items[].commission.attributedInflow | Object | Cumulative valid attributed inflow, using the AssetAmount structure. |
items[].commission.attributionRate | Number | Attribution ratio on the Share-Seconds basis. |
items[].commission.attributionRateBasis | String | Attribution ratio calculation basis. |
items[].commission.timeWeightedTvl | Object | Time-weighted TVL, includes valuation reliability status. |
items[].commission.components | Array | Commission details split by fee, asset, and status. |
AssetAmount, TimeWeightedTvl, and CommissionComponent:
| Structure | Field | Type | Description |
|---|---|---|---|
| AssetAmount | assetAddress | String | Asset address. |
| AssetAmount | assetDecimals | Integer | Asset decimals. |
| AssetAmount | amount | String | Raw on-chain asset quantity. |
| TimeWeightedTvl | assetAddress | String | Asset address. |
| TimeWeightedTvl | assetDecimals | Integer | Asset decimals. |
| TimeWeightedTvl | amount | String | Raw on-chain asset quantity. |
| TimeWeightedTvl | valuationStatus | String | Valuation reliability status. |
| CommissionComponent | feeType | String | Fee type. |
| CommissionComponent | assetType | String | Commission asset type. |
| CommissionComponent | assetAddress | String | Commission asset address. |
| CommissionComponent | assetDecimals | Integer | Commission asset decimals. |
| CommissionComponent | amount | String | Raw on-chain commission asset quantity. |
| CommissionComponent | status | String | Commission ledger status. |
Response example
{
"code": "200",
"message": "SUCCESS",
"timestamp": 1786588800123,
"data": {
"dataAsOf": "2026-08-13T08:00:00Z",
"vault": {
"chainId": 1,
"vaultAddress": "0x1111111111111111111111111111111111111111",
"name": "Example Vault",
"symbol": "vUSDC",
"assetAddress": "0x3333333333333333333333333333333333333333",
"assetDecimals": 6
},
"channel": {
"channelCode": "CHANNEL_EXAMPLE",
"name": "Example Channel",
"status": "ACTIVE",
"payoutWallet": {
"address": "0x4444444444444444444444444444444444444444",
"chainCode": "ETH"
}
},
"items": [
{
"refcode": {
"code": "REF_EXAMPLE",
"label": "Example Refcode",
"status": "ACTIVE"
},
"distribution": {
"enabled": true,
"status": "ACTIVE",
"effectiveRates": {
"managementRate": 20.0,
"performanceRate": 15.0
},
"rateSource": "CHANNEL",
"effectivePeriod": {
"startTime": "2026-08-01T00:00:00Z"
}
},
"commission": {
"attributedInflow": {
"assetAddress": "0x3333333333333333333333333333333333333333",
"assetDecimals": 6,
"amount": "2500000000"
},
"attributionRate": 12.5,
"attributionRateBasis": "SHARE_SECONDS",
"timeWeightedTvl": {
"assetAddress": "0x3333333333333333333333333333333333333333",
"assetDecimals": 6,
"amount": "1800000000",
"valuationStatus": "AVAILABLE"
},
"components": [
{
"feeType": "MANAGEMENT_FEE",
"assetType": "UNDERLYING",
"assetAddress": "0x3333333333333333333333333333333333333333",
"assetDecimals": 6,
"amount": "12000000",
"status": "ACCRUED"
}
]
}
}
]
}
}
Specific limits
- A partner can only query channels authorized to it by the platform.
- Amount strings are raw on-chain asset quantities; partners should display them together with the corresponding
assetDecimals. - Rate values are percentages, e.g.
20.0means 20%, not 0.2. - The response does not include recalculated commission, cross-asset totals, or USD valuation.
7.2 Query Channel Attribution Flows
Purpose
Paginated query of attributed inflow and outflow records for a given channel and Vault.
Method + Path
GET /api/r25/channel-distribution/transactions
Request location
Query; no request body.
Request fields
| Field | Type | Required | Default/Enum | Description |
|---|---|---|---|---|
channelCode | String | Yes | None | Channel stable code; leading and trailing whitespace is trimmed before use. |
refcode | String | No | None | Refcode; must not be blank-only when provided. |
vaultAddress | String | Yes | 0x + 40 hexadecimal characters | Core Vault contract address; lowercased before querying. |
transactionType | String | No | deposit / withdraw | Case-sensitive. |
pageNumber | Integer | No | Default 1; min 1 | The request parameter name is pageNumber. |
pageSize | Integer | No | Default 20; range 1..100 | Page size. |
Request example
curl --get 'https://<api-host>/api/r25/channel-distribution/transactions' \
--data-urlencode 'channelCode=CHANNEL_EXAMPLE' \
--data-urlencode 'vaultAddress=0x1111111111111111111111111111111111111111' \
--data-urlencode 'transactionType=deposit' \
--data-urlencode 'pageNumber=1' \
--data-urlencode 'pageSize=20' \
-H 'X-Client-Id: <client-id>' \
-H 'X-Timestamp: <unix-seconds>' \
-H 'X-Signature: <128-char-ed25519-signature-hex>'
data** response field description**
| Field | Type | Description |
|---|---|---|
pageNum | Integer | Current page number. Note that the response field name differs from the request's pageNumber. |
pageSize | Integer | Current page size. |
hasNext | Boolean | Whether a next page exists. |
items | Array | Current page of flows. |
items[].eventId | String | Unique identifier of the rebate-chain event. |
items[].chainId | Long | Chain ID. |
items[].txHash | String | Transaction hash. |
items[].blockNumber | Long | Block height. |
items[].transactionIndex | Integer | Transaction index within the block. |
items[].logIndex | Integer | Log index of the transaction. |
items[].time | String | On-chain event time. |
items[].type | String | deposit or withdraw. |
items[].refcode | String | Attributed refcode. |
items[].wallet | String | Masked wallet address, usually keeping only the first 6 and the last 4 characters. |
items[].amount.assetAddress | String | Base asset address. |
items[].amount.assetDecimals | Integer | Base asset decimals. |
items[].amount.amount | String | Raw on-chain base asset quantity. |
items[].shares | String | Corresponding raw share quantity. |
Response example
{
"code": "200",
"message": "SUCCESS",
"timestamp": 1786588800123,
"data": {
"pageNum": 1,
"pageSize": 20,
"hasNext": false,
"items": [
{
"eventId": "evt_example_001",
"chainId": 1,
"txHash": "0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
"blockNumber": 23456789,
"transactionIndex": 12,
"logIndex": 3,
"time": "2026-08-13T08:00:00Z",
"type": "deposit",
"refcode": "REF_EXAMPLE",
"wallet": "0x1111...1111",
"amount": {
"assetAddress": "0x3333333333333333333333333333333333333333",
"assetDecimals": 6,
"amount": "1000000"
},
"shares": "975609756097560975"
}
]
}
}
Specific limits
- A partner can only query channels authorized to it by the platform.
- The response does not output the total count; whether a next page exists is indicated only by
hasNext. walletis masked and does not return the full holder address; an abnormally short value returns****.
7.3 Batch Query Channel Merkle Proofs
Purpose
Paginated query of the claimable amount and Merkle proofs of a given channel and multiple Vaults, scoped by the caller's Curator permission.
Method + Path
GET /api/r25/channel-distribution/merkle-proofs/batch
Request location
Query; no request body.
Request fields
| Field | Type | Required | Default/Restriction | Description |
|---|---|---|---|---|
channelAddress | String | Yes | 0x + 40 hexadecimal characters | Channel login address; lowercased before querying. |
vaultAddress | String[] | Yes | Each item is 0x + 40 hexadecimal characters | Vault on-chain addresses; multiple values are comma-separated, and the same Query Key must not be repeated. The service converts them to the Vault IDs required by Rebate. |
pageNumber | Integer | No | Default 1; min 1 | Current page number. |
pageSize | Integer | No | Default 20; range 1..100 | Page size. |
Request example
curl --get 'https://<api-host>/api/r25/channel-distribution/merkle-proofs/batch' \
--data-urlencode 'channelAddress=0x9b82166ff1a8ad0194d06a08f1738ffb0a367ca5' \
--data-urlencode 'vaultAddress=0x1111111111111111111111111111111111111111,0x2222222222222222222222222222222222222222' \
--data-urlencode 'pageNumber=1' \
--data-urlencode 'pageSize=20' \
-H 'X-Client-Id: <client-id>' \
-H 'X-Timestamp: <unix-seconds>' \
-H 'X-Signature: <128-char-ed25519-signature-hex>'
data** response field description**
| Field | Type | Description |
|---|---|---|
authorizedVaultIds | Long[] | Vault external IDs that the current Curator is authorized to query. |
pageSize | Integer | Current page size. |
pageNumber | Integer | Current page number. |
hasNext | Boolean | Whether a next page exists. |
proofs | Array | Merkle Proof results in the current page. |
proofs[].vaultId | Long | Vault external ID. |
proofs[].address | String | Actual channel rebate receiving address used to generate the proof. |
proofs[].amount | String | Claimable amount in minimum-unit integer. |
proofs[].proof | String[] | Merkle Proof hash path; can be an empty array when the leaf is the root. |
proofs[].merkleRoot | String | Merkle root of the latest ALLOCATED batch. |
proofs[].claimAddress | String | Corresponding Vault project_fee_treasury contract address. |
Response example
{
"code": "200",
"message": "SUCCESS",
"timestamp": 1788182198245,
"data": {
"authorizedVaultIds": [900464],
"pageSize": 20,
"pageNumber": 1,
"hasNext": false,
"proofs": [
{
"vaultId": 900464,
"address": "0x9b82166ff1a8ad0194d06a08f1738ffb0a367ca5",
"amount": "759505619177563",
"proof": [
"8dfcb39fc220c662db9bd81034f6676e36f8445eb1aa6ba308b04e942973b08c"
],
"merkleRoot": "1b08f4b461226f63683fabe885d9a986db1b35cb869b00e4126275b010ca4065",
"claimAddress": "0x146a8d35e64d3919994749561a33dc8b96e5862d"
}
]
}
}
Specific limits
- The Curator address comes from the verified calling identity and cannot be specified or replaced via request parameters.
- The response does not include
currentCountortotalCount; usehasNextto determine whether another page is available. amountis a minimum-unit on-chain integer string and must not be parsed as a floating-point number.
8. Transaction Records Endpoint
8.1 Query Transaction Records by Conditions
Purpose
Paginated query of the Vault on-chain transaction event records currently integrated, filterable by business owner wallet address, Vault contract address, transaction type, status, and async request ID.
Method + Path
GET /api/r25/standard-vault/transaction/records
Request location
Query; no request body.
Request fields
| Field | Type | Required | Default/Enum | Description |
|---|---|---|---|---|
address | String | No | 0x + 40 hexadecimal characters | Business owner wallet address; leading and trailing whitespace is trimmed and lowercased before querying. |
vaultAddress | String | No | 0x + 40 hexadecimal characters | Vault contract address; leading and trailing whitespace is trimmed and lowercased before querying. |
recordType | String | No | DEPOSIT / REDEEM | Case-insensitive; an empty string means no filter; other values fail validation. |
status | String | No | PENDING / CLAIMED / CANCELLED | Case-insensitive; an empty string means no filter; other values fail validation. |
requestId | String | No | None | Async request ID; a blank value is treated as no filter. |
pageNumber | Integer | No | Default 1; min 1 | Page number. |
pageSize | Integer | No | Default 100; range 1..100 | Page size. |
Request example
curl --get 'https://<api-host>/api/r25/standard-vault/transaction/records' \
--data-urlencode 'address=0x1111111111111111111111111111111111111111' \
--data-urlencode 'vaultAddress=0x2222222222222222222222222222222222222222' \
--data-urlencode 'recordType=deposit' \
--data-urlencode 'status=claimed' \
--data-urlencode 'pageNumber=1' \
--data-urlencode 'pageSize=100' \
-H 'X-Client-Id: <client-id>' \
-H 'X-Timestamp: <unix-seconds>' \
-H 'X-Signature: <128-char-ed25519-signature-hex>'
data** response field description**
| Field | Type | Description |
|---|---|---|
list | Array | Transaction records in the current page. |
pageNumber | Integer | Current page number. |
pageSize | Integer | Current page size. |
total | Long | Total number of matching records. |
hasNext | Boolean | Whether a next page exists. |
list[].requestTxHash | String | Request transaction hash. |
list[].requestType | String | DEPOSIT or REDEEM. |
list[].vaultAddress | String | Vault contract address. |
list[].status | String | PENDING, CLAIMED, or CANCELLED. |
list[].assets | String | Asset quantity, raw on-chain un-scaled integer string. |
list[].shares | String | Share quantity, raw on-chain un-scaled integer string. |
list[].chainId | Long | Chain ID. |
list[].requestBlockNumber | Long | Block height of the request transaction. |
list[].requestBlockTime | Long | Request transaction block time, epoch seconds. |
list[].requestId | String | Async request ID; null for synchronous transactions. |
list[].settleTxHash | String | Settlement transaction hash; null when not settled. |
list[].settleBlockTime | Long | Settlement transaction block time, epoch seconds; null when not settled. |
list[].walletAddress | String | Business owner user wallet address. |
Response example
{
"code": "200",
"message": "SUCCESS",
"timestamp": 1786588800123,
"data": {
"list": [
{
"requestTxHash": "0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
"requestType": "DEPOSIT",
"vaultAddress": "0x2222222222222222222222222222222222222222",
"status": "CLAIMED",
"assets": "1000000",
"shares": "975609756097560975",
"chainId": 1,
"requestBlockNumber": 23456789,
"requestBlockTime": 1786588700,
"requestId": null,
"settleTxHash": "0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
"settleBlockTime": 1786588700,
"walletAddress": "0x1111111111111111111111111111111111111111"
}
],
"pageNumber": 1,
"pageSize": 100,
"total": 1,
"hasNext": false
}
}
Specific limits
assetsandsharesare raw on-chain un-scaled integer strings; when the client needs to compute, they should be parsed with an arbitrary-precision integer type.- Results are returned in descending order of request block height and transaction index within the block; records at the same position retain a deterministic order.
recordTypeandstatusallow only the current business enums listed in the table; any other non-empty value returns-1005.- Both
addressandvaultAddressare optional. When no business filter is provided, all available records are returned according to the pagination parameters.
9. Trading REST Write Endpoints
9.1 Common Request Envelope and Constraints
Trading REST provides order creation, order cancellation, and position closing. None of the three endpoints accept query parameters; their request bodies use the same envelope:
{
"ts": 1786588800123,
"exchange": "HYPERLIQUID",
"data": {
"vaultAddress": "0x2222222222222222222222222222222222222222"
}
}
| Field | Type | Required | Description |
|---|---|---|---|
ts | Long | Yes | Request generation time, Unix milliseconds. A request older than 10 seconds before the server's current time returns INVALID_REQUEST; regenerate it on every request or retry. Do not send a future time. |
exchange | String | Yes | Use uppercase HYPERLIQUID or HABITTRADE; values are case-sensitive. |
data | Object | Yes | The business parameter object for the current operation. The request fails when it is missing or is not a JSON Object. |
data.vaultAddress | String | Yes | Vault contract address. Leading and trailing whitespace is removed and the value is converted to lowercase. The Vault must be within the operating permissions of the current X-Client-Id. |
In addition to the body ts, the request must still carry X-Client-Id, X-Timestamp, and X-Signature from Section 3. The signature digest must be computed from the final sent raw bytes of the complete envelope, not from data only.
Decimal values such as quantity, price, and leverage should be JSON strings to avoid floating-point precision loss. maxSlippageBps may be a JSON integer or an integer string; 50 means 50 bps, i.e. 0.5%.
Trading REST currently does not accept clientOrderId and provides no business-level idempotency key. Authentication replay protection can only reject requests with completely identical signing material within the time window; resending with a changed ts or body is not the same replay. Therefore, when you receive UPSTREAM_RESULT_UNKNOWN, a network timeout, or the client gets no response, you must not directly repeat the order or close; first confirm the previous result via the order/position query capability.
9.2 Create Order
Method + Path
POST /api/r25/trade/order/create
data** request fields**
| Field | Type | Hyperliquid | HabitTrade | Description |
|---|---|---|---|---|
vaultAddress | String | Required | Required | Vault contract address; the current caller must be authorized to operate it. |
symbol | String | Required | Required | Trading asset identifier, used as provided. |
side | String | Required | Required | BUY or SELL, case-insensitive. |
orderType | String | Required | Required | LIMIT or MARKET, case-insensitive. |
quantity | String | Required | Required | Order quantity decimal string; must conform to the valid quantity precision of the current exchange. |
price | String | Required for LIMIT | Should be provided for LIMIT | Limit price. Hyperliquid rejects a request when this field is missing; valid HabitTrade values follow its trading rules. |
maxSlippageBps | Integer / String | Required for MARKET | Required | Maximum slippage in BPS. Hyperliquid MARKET allows 1..1000; HabitTrade uses this value as both maximum loss and maximum slippage, and its valid range follows HabitTrade rules. |
leverage | String | Required | Not used | Leverage multiplier represented as a decimal string. |
timeInForce | String | Optional | Not used | Hyperliquid time-in-force value; supported values must be agreed with the exchange. |
reduceOnly | Boolean | Optional, default false | Not used | Reduce-only flag. |
tokenAddress | String | Not used | Optional | HabitTrade asset contract address; whether omission is accepted follows HabitTrade rules. |
Request example: Hyperliquid market order
curl 'https://<api-host>/api/r25/trade/order/create' \
-X POST \
-H 'Content-Type: application/json' \
-H 'X-Client-Id: <client-id>' \
-H 'X-Timestamp: <unix-seconds-or-milliseconds>' \
-H 'X-Signature: <128-char-ed25519-signature-hex>' \
--data-raw '{"ts":1786588800123,"exchange":"HYPERLIQUID","data":{"vaultAddress":"0x2222222222222222222222222222222222222222","symbol":"BTC","side":"BUY","orderType":"MARKET","quantity":"0.01","maxSlippageBps":50,"leverage":"5","reduceOnly":false}}'
The body in --data-raw is only a structural example; the actual call must use the current ts and sign on the completely identical raw body bytes.
Success response
{
"code": "200",
"message": "SUCCESS",
"timestamp": 1786588800456,
"data": {
"orderId": "HYPERLIQUID:123456789",
"exchangeOrderId": "123456789",
"status": "PENDING",
"acceptedAt": 1786588800400,
"encodedData": null
}
}
| Field | Type | Description |
|---|---|---|
orderId | String | Platform-normalized order ID, usually <exchange>:<exchangeOrderId>; may be null when no exchange order ID is available. |
exchangeOrderId | String | Exchange order ID; may be null when no value is available. |
status | String | PENDING when Hyperliquid successfully accepts; BUILT when HabitTrade successfully builds the transaction to be signed. |
acceptedAt | Long | Acceptance time in Unix milliseconds. Hyperliquid uses the exchange-provided time when available, otherwise the API response time; HabitTrade uses the API response time. |
encodedData | String | ABI-encoded data to be signed returned by HabitTrade; Hyperliquid returns null. |
HabitTrade's BUILT only means the transaction parameters have been built; the partner still needs to complete signing and on-chain submission per the mutual agreement, and must not treat it as on-chain settlement.
9.3 Cancel Order
Method + Path
POST /api/r25/trade/order/cancel
Currently only Hyperliquid supports cancellation; exchange=HABITTRADE always returns FEATURE_UNSUPPORTED.
data** request fields**
| Field | Type | Required | Description |
|---|---|---|---|
vaultAddress | String | Yes | Vault contract address; the current caller must be authorized to operate it. |
exchangeOrderId | String | Yes | Hyperliquid order ID, must be parseable as a Long. When passing the platform orderId, strip the HYPERLIQUID: prefix first. |
symbol | String | Yes | Trading asset identifier, used as provided. |
Request example
curl 'https://<api-host>/api/r25/trade/order/cancel' \
-X POST \
-H 'Content-Type: application/json' \
-H 'X-Client-Id: <client-id>' \
-H 'X-Timestamp: <unix-seconds-or-milliseconds>' \
-H 'X-Signature: <128-char-ed25519-signature-hex>' \
--data-raw '{"ts":1786588800123,"exchange":"HYPERLIQUID","data":{"vaultAddress":"0x2222222222222222222222222222222222222222","exchangeOrderId":"123456789","symbol":"BTC"}}'
Success response
{
"code": "200",
"message": "SUCCESS",
"timestamp": 1786588800456,
"data": {
"exchangeOrderId": "123456789",
"status": "CANCELED"
}
}
A successful response means the cancellation request received a successful result. If you receive UPSTREAM_RESULT_UNKNOWN, query the order status first; do not assume that cancellation either failed or succeeded.
9.4 Close Position
Method + Path
POST /api/r25/trade/position/close
data** request fields**
| Field | Type | Hyperliquid | HabitTrade | Description |
|---|---|---|---|---|
vaultAddress | String | Required | Required | Vault contract address; the current caller must be authorized to operate it. |
symbol | String | Required | Required | Asset identifier to close, used as provided. |
quantity | String | Optional | Optional | Partial-close quantity represented as a decimal string and subject to quantity precision rules. When omitted, the request is treated as a full close. |
maxSlippageBps | Integer / String | Required, 1..1000 | Required | Maximum slippage in BPS; the valid HabitTrade range follows its trading rules. |
closeMode | String | Optional | Not used | For Hyperliquid, explicitly passing PARTIAL returns FEATURE_UNSUPPORTED; FULL or an omitted value may be used with the optional quantity. For HabitTrade, the presence of quantity determines whether the close is full or partial. |
tokenAddress | String | Not used | Optional | HabitTrade asset contract address. |
Request example: HabitTrade full close
curl 'https://<api-host>/api/r25/trade/position/close' \
-X POST \
-H 'Content-Type: application/json' \
-H 'X-Client-Id: <client-id>' \
-H 'X-Timestamp: <unix-seconds-or-milliseconds>' \
-H 'X-Signature: <128-char-ed25519-signature-hex>' \
--data-raw '{"ts":1786588800123,"exchange":"HABITTRADE","data":{"vaultAddress":"0x2222222222222222222222222222222222222222","symbol":"ETH","tokenAddress":"0x3333333333333333333333333333333333333333","maxSlippageBps":50}}'
A successful close-position response has the same structure as the create-order response in Section 9.2. Hyperliquid returns status=PENDING and encodedData=null; HabitTrade returns status=BUILT with the corresponding encodedData.
10. Webhook Callbacks
10.1 Actual Callback Request
The platform sends JSON via POST to the configured callbackUrl:
POST /openapi/events HTTP/1.1
Host: hooks.partner.example
Content-Type: application/json
Authorization: Bearer <callbackApiKey>
X-Delivery-Id: <deliveryId>
X-Timestamp: <unix-seconds>
Header descriptions:
| Header | Description |
|---|---|
Content-Type | Fixed as application/json. |
Authorization | Fixed as Bearer <callbackApiKey> and uses the callback API key configured by both parties. |
X-Delivery-Id | The unique ID of this logical delivery; it stays the same across retries of the same delivery, and partners must use it for idempotency. |
X-Timestamp | Unix seconds at the time the platform sends the request. |
The Webhook currently always carries a Bearer API Key and does not send HMAC or other signature headers. Partners should protect the transport with HTTPS and use X-Delivery-Id for deduplication; to restrict the source, configure a network-layer whitelist.
10.2 DEPLOYED / GRAY / OFFICIAL Payload
All three event types use the same payload structure. Notifications are sent to active subscriptions of the partner to which the Vault belongs, provided those subscriptions include the corresponding event type. The stages are separate events: subscribing only to DEPLOYED does not include GRAY or OFFICIAL. Deduplicate retries by X-Delivery-Id, not by Vault address alone.
Field descriptions:
| Field | Type | Description |
|---|---|---|
eventType | String | Vault lifecycle event: DEPLOYED (deployment completed), GRAY (canary publication), or OFFICIAL (official publication). |
occurredAt | String | ISO 8601 offset time in Asia/Hong_Kong. |
data.vaultAddress | String | The Vault contract address associated with the event, normalized to lowercase. |
data.metadataPath | String | Relative path to query the latest metadata; the address value is URL-encoded. |
{
"eventType": "DEPLOYED",
"occurredAt": "2026-08-13T16:00:00+08:00",
"data": {
"vaultAddress": "0x1111111111111111111111111111111111111111",
"metadataPath": "/api/r25/standard-vault/metadata?vaultAddress=0x1111111111111111111111111111111111111111"
}
}
GRAY example:
{
"eventType": "GRAY",
"occurredAt": "2026-08-13T16:00:00+08:00",
"data": {
"vaultAddress": "0x1111111111111111111111111111111111111111",
"metadataPath": "/api/r25/standard-vault/metadata?vaultAddress=0x1111111111111111111111111111111111111111"
}
}
OFFICIAL example:
{
"eventType": "OFFICIAL",
"occurredAt": "2026-08-13T16:00:00+08:00",
"data": {
"vaultAddress": "0x1111111111111111111111111111111111111111",
"metadataPath": "/api/r25/standard-vault/metadata?vaultAddress=0x1111111111111111111111111111111111111111"
}
}
On receipt, you can append metadataPath to https://<api-host> and re-sign a call to the query endpoint per Section 3.
10.3 Channel and Refcode Change Payload
CHANNEL_CREATED, CHANNEL_UPDATED, and CHANNEL_STATUS_CHANGED use:
{
"eventType": "CHANNEL_UPDATED",
"occurredAt": "2026-08-13T16:00:00+08:00",
"data": {
"channelCode": "CHANNEL_EXAMPLE",
"refcode": null,
"commissionPath": "/api/r25/channel-distribution/commission"
}
}
REFCODE_CREATED and REFCODE_UPDATED also include refcode:
{
"eventType": "REFCODE_CREATED",
"occurredAt": "2026-08-13T16:00:00+08:00",
"data": {
"channelCode": "CHANNEL_EXAMPLE",
"refcode": "REF_EXAMPLE",
"commissionPath": "/api/r25/channel-distribution/commission"
}
}
Field descriptions:
| Field | Type | Applicable events | Description |
|---|---|---|---|
eventType | String | All | Specific Rebate event type. |
occurredAt | String | All | ISO 8601 offset time in Asia/Hong_Kong. |
data.channelCode | String | All channel/refcode events | Channel code. |
data.refcode | String | Refcode events only | Refcode; null for other channel events. |
data.commissionPath | String | All | Fixed as /api/r25/channel-distribution/commission. |
10.4 Rate and Distribution Plan Change Payload
CHANNEL_REBATE_RATE_CHANGED includes both the channel and the Vault:
{
"eventType": "CHANNEL_REBATE_RATE_CHANGED",
"occurredAt": "2026-08-13T16:00:00+08:00",
"data": {
"channelCode": "CHANNEL_EXAMPLE",
"vaultAddress": "0x1111111111111111111111111111111111111111",
"commissionPath": "/api/r25/channel-distribution/commission"
}
}
VAULT_DISTRIBUTION_PLAN_CHANGED only provides Vault locating information, and channelCode returns null:
{
"eventType": "VAULT_DISTRIBUTION_PLAN_CHANGED",
"occurredAt": "2026-08-13T16:00:00+08:00",
"data": {
"channelCode": null,
"vaultAddress": "0x1111111111111111111111111111111111111111",
"commissionPath": "/api/r25/channel-distribution/commission"
}
}
Field descriptions:
| Field | Type | Applicable events | Description |
|---|---|---|---|
eventType | String | All | Specific Rebate event type. |
occurredAt | String | All | ISO 8601 offset time in Asia/Hong_Kong. |
data.channelCode | String | CHANNEL_REBATE_RATE_CHANGED | Channel code; null in Vault distribution plan events. |
data.vaultAddress | String | All | Vault contract address. |
data.commissionPath | String | All | Fixed as /api/r25/channel-distribution/commission. |
10.5 Delivery Success, Timeouts, and Retries
- Any HTTP
2xxreturned by the partner is treated as a successful receipt; the response body does not participate in the success determination. - The platform connection timeout is 3 seconds; the response read timeout is 10 seconds; HTTP redirects are not followed.
- The platform reads at most the first 4000 bytes of the partner's response body.
- A single delivery is attempted at most 3 times, including the first. After a recoverable failure, the first retry waits 60 seconds and the second waits 300 seconds.
- HTTP
401,403, and410immediately terminate the delivery and disable the corresponding Webhook configuration. - A callback request that cannot be constructed, or a callback address that fails the pre-delivery security review, terminates the delivery and disables the corresponding Webhook configuration.
- Other failures are terminated after the 3rd attempt. After the partner recovers, terminated deliveries are not retried automatically.
- Because network timeouts may cause "the partner has processed the request but the platform received no response", you must use
X-Delivery-Idfor idempotent processing and return2xxas quickly as possible.
11. Integration Notes and Current Limitations
- The
vaultAddressof/api/r25/standard-vault/metadatais required by the API contract; a missing, blank, or unknown address returns-1010. - All response fields documented here are included; fields with no available value are returned as
null. - All business endpoints require Ed25519 V2 authentication. The signature is based on the raw body bytes and the canonical query; rewriting the request by a proxy layer, SDK, or serializer may cause signature verification to fail.
- The HTTP status of business responses remains
200; you must checkcodein the response JSON. - The Webhook callback always carries
Authorization: Bearer <callbackApiKey>and currently sends no HMAC or other signature header. Use HTTPS, source-side network controls (where applicable), andX-Delivery-Idto protect the receiver with idempotency. - Do not use JSON floating-point numbers for on-chain amounts marked as String in this document; decimal JSON numbers should also be parsed with an arbitrary-precision decimal type.