Alibaba Cloud Marketplace — Token Consumption Tracking Guide for Partners
This guide walks you through the implementation of token consumption tracking (metering) for products listed on the Alibaba Cloud Marketplace that invoke Alibaba Cloud Model Studio (Bailian) APIs. Proper tracking ensures your product qualifies as a MaaS (Model-as-a-Service) product, which unlocks platform-level sales support, promotional resources, and accurate revenue attribution.
Why this matters: Products without proper tracking cannot be classified as MaaS products, which results in reduced sales effort and fewer platform resources allocated to your listing.
1. Overview
When your product calls Bailian APIs (e.g., Qwen models), you need to attach a special HTTP header x-dashscope-euid to every API request. This header identifies the end customer so that token consumption is correctly attributed to their account and to your product.
The implementation has three moving parts:
Receive customer identity via an SPI callback (or License API) when a customer purchases your product on the Marketplace.
Store the mapping between the customer and their productCode + aliUid in your system.
Attach the tracking header to every Bailian API call, dynamically populated from your stored mapping.
Estimated implementation effort: 1–2 developer days.
2. The x-dashscope-euid Header
Every request to the Bailian API must include the following header:
x-dashscope-euid: <JSON string>The JSON payload contains exactly five fields. All five are required.
Field | Type | Fixed? | Value |
bizType | String | Fixed | B2B |
moduleType | String | Fixed | Third-partyproducts |
moduleCode | String | Dynamic | market_${productCode} — productCode comes from the SPI callback |
accountType | String | Fixed | Aliyun |
accountId | String | Dynamic | The customer's Alibaba Cloud account ID (aliUid) from the SPI callback |
Example (marketplace customer — "narrow scope"):
{
"bizType": "B2B",
"moduleType": "Third-partyproducts",
"moduleCode": "market_abc123",
"accountType": "Aliyun",
"accountId": "1234567890"
}Example (non-marketplace customer — "broad scope" fallback):
{
"bizType": "B2B",
"moduleType": "Third-partyproducts",
"moduleCode": "market_cmapi00069878",
"accountType": "Aliyun",
"accountId": ""
}The "broad scope" fallback is used when a user calling your service is not found in your mapping table (i.e., they did not purchase through the Marketplace). In this case, moduleCode uses a hardcoded productCode (visible in your ISV console after publishing), and accountId is an empty string.
Case Sensitivity Rules
Component | Case-Sensitive? | Notes |
Header key name | No | x-dashscope-euid and X-DashScope-EUID are both accepted |
JSON field names (keys) | Yes | Must use exact camelCase: bizType, moduleType, etc. |
JSON field values | No | B2B and b2b are both recognized |
3. Obtaining Dynamic Values via SPI Callbacks
The values for productCode and accountId (aliUid) come from the Marketplace at the time a customer purchases your product. The mechanism differs by product type.
3A. SaaS Products — SPI Delivery
When publishing your SaaS product, enable "Production API Notification" and configure your SPI callback URL. Select at least the createInstance event.
After a customer completes payment, the Marketplace sends an HTTP GET request to your callback URL:
GET https://your-domain.com/spi/callback?action=createInstance&aliUid=123456&orderBizId=xxx&productCode=abc123&...Key parameters you need to capture and store:
Parameter | Description |
productCode | Your product code — used in moduleCode |
aliUid | Customer's Alibaba Cloud account ID |
orderBizId | Unique business instance ID for idempotency |
Store the mapping orderBizId → (productCode, aliUid) in your database and associate it with the customer's account in your system.
Important: Your SPI endpoint must be idempotent (deduplicate on orderBizId) and respond within 3 seconds. The Marketplace retries up to 120 times on failure.
3B. SaaS Products — License Code Delivery
For license-code products, dynamic values are obtained via the DescribeLicense API instead of SPI callbacks.
When a customer activates their license, call:
DescribeLicense(LicenseCode = "<customer's license code>")Extract the following from the response:
License.ProductCode → used in moduleCode
License.ExtendInfo.AliUid → used in accountId
Store these values and bind them to the customer's account for subsequent Bailian API calls.
3C. API Products — orderPay SPI
For API products, configure the Payment Success (orderPay) SPI callback in your ISV console. After a customer pays, the Marketplace pushes:
Parameter | Description |
action | Fixed value: orderPay |
aliUid | Customer's Alibaba Cloud account ID |
orderBizId | Business instance ID = CaCloudMarketInstanceId |
productCode | Your product code |
You must respond with {"success":true} and ensure idempotency based on requestId.
Critical mapping: orderBizId from the SPI callback is the same value as CaCloudMarketInstanceId that the API Gateway passes through to your backend. Configure your API Gateway to forward CaCloudMarketInstanceId as a backend header, then look up your mapping table to retrieve the corresponding productCode and aliUid.
4. Runtime Logic
At runtime, when a customer triggers a Bailian API call through your product:
Customer invokes your service
↓
Look up the customer in your mapping table
↓
┌────┴────┐
│ Found? │
└────┬────┘
Yes ↓ No ↓
↓ ↓
Narrow scope Broad scope (fallback)
moduleCode = moduleCode = market_<hardcoded productCode>
market_<stored productCode>
accountId = accountId = ""
<stored aliUid>
↓ ↓
└──────┬───────┘
↓
Assemble x-dashscope-euid header
↓
Call Bailian API with the header5. Code Examples
Python (requests library)
import requests
import json
url = "https://dashscope.aliyuncs.com/compatible-mode/v1/chat/completions"
headers = {
"Authorization": "Bearer sk-xxx",
"Content-Type": "application/json",
"x-dashscope-euid": json.dumps({
"bizType": "B2B",
"moduleType": "Third-partyproducts",
"moduleCode": "market_abc123",
"accountType": "Aliyun",
"accountId": "1234567890"
})
}
payload = {
"model": "qwen-plus",
"messages": [
{"role": "system", "content": "You are a helpful assistant."},
{"role": "user", "content": "Hello"}
]
}
response = requests.post(url, headers=headers, json=payload)
print(response.json())Python (OpenAI SDK)
from openai import OpenAI
client = OpenAI(
api_key="sk-xxx",
base_url="https://dashscope.aliyuncs.com/compatible-mode/v1",
default_headers={
"x-dashscope-euid": '{"bizType":"B2B","moduleType":"Third-partyproducts","moduleCode":"market_abc123","accountType":"Aliyun","accountId":"1234567890"}'
}
)
response = client.chat.completions.create(
model="qwen-plus",
messages=[
{"role": "system", "content": "You are a helpful assistant."},
{"role": "user", "content": "Hello"}
]
)
print(response.choices[0].message.content)Java (OkHttp)
import okhttp3.*;
public class BailianTrackingExample {
public static void main(String[ ] args) throws Exception {
OkHttpClient client = new OkHttpClient();
String euidJson = "{\"bizType\":\"B2B\",\"moduleType\":\"Third-partyproducts\","
+ "\"moduleCode\":\"market_abc123\",\"accountType\":\"Aliyun\","
+ "\"accountId\":\"1234567890\"}";
String body = "{\"model\":\"qwen-plus\",\"messages\":["
+ "{\"role\":\"user\",\"content\":\"Hello\"}]}";
Request request = new Request.Builder()
.url("https://dashscope.aliyuncs.com/compatible-mode/v1/chat/completions")
.post(RequestBody.create(body, MediaType.parse("application/json")))
.addHeader("Authorization", "Bearer sk-xxx")
.addHeader("Content-Type", "application/json")
.addHeader("x-dashscope-euid", euidJson)
.build();
Response response = client.newCall(request).execute();
System.out.println(response.body().string());
}
}Node.js (fetch)
const response = await fetch(
"https://dashscope.aliyuncs.com/compatible-mode/v1/chat/completions",
{
method: "POST",
headers: {
"Authorization": "Bearer sk-xxx",
"Content-Type": "application/json",
"x-dashscope-euid": JSON.stringify({
bizType: "B2B",
moduleType: "Third-partyproducts",
moduleCode: "market_abc123",
accountType: "Aliyun",
accountId: "1234567890"
})
},
body: JSON.stringify({
model: "qwen-plus",
messages: [
{ role: "user", content: "Hello" }
]
})
}
);
const data = await response.json();
console.log(data);Go
package main
import (
"bytes"
"encoding/json"
"fmt"
"net/http"
)
func main() {
euid := map[string]string{
"bizType": "B2B",
"moduleType": "Third-partyproducts",
"moduleCode": "market_abc123",
"accountType": "Aliyun",
"accountId": "1234567890",
}
euidBytes, _ := json.Marshal(euid)
body := map[string]interface{}{
"model": "qwen-plus",
"messages": [ ]map[string]string{
{"role": "user", "content": "Hello"},
},
}
bodyBytes, _ := json.Marshal(body)
req, _ := http.NewRequest("POST",
"https://dashscope.aliyuncs.com/compatible-mode/v1/chat/completions",
bytes.NewBuffer(bodyBytes))
req.Header.Set("Authorization", "Bearer sk-xxx")
req.Header.Set("Content-Type", "application/json")
req.Header.Set("x-dashscope-euid", string(euidBytes))
resp, err := http.DefaultClient.Do(req)
if err != nil {
panic(err)
}
defer resp.Body.Close()
var result map[string]interface{}
json.NewDecoder(resp.Body).Decode(&result)
fmt.Println(result)
}Dynamic Header Construction (Java / Spring)
For production use, you should dynamically assemble the header based on the current customer:
import org.springframework.http.*;
import org.springframework.web.client.RestTemplate;
public class BailianService {
private final RestTemplate restTemplate = new RestTemplate();
public String callModel(String productCode, String aliUid) {
HttpHeaders headers = new HttpHeaders();
headers.set("Authorization", "Bearer sk-xxx");
headers.set("Content-Type", "application/json");
String euid = String.format(
"{\"bizType\":\"B2B\",\"moduleType\":\"Third-partyproducts\","
+ "\"moduleCode\":\"market_%s\",\"accountType\":\"Aliyun\","
+ "\"accountId\":\"%s\"}",
productCode, aliUid
);
headers.set("x-dashscope-euid", euid);
String body = "{\"model\":\"qwen-plus\",\"messages\":["
+ "{\"role\":\"user\",\"content\":\"Hello\"}]}";
HttpEntity<String> entity = new HttpEntity<>(body, headers);
ResponseEntity<String> response = restTemplate.postForEntity(
"https://dashscope.aliyuncs.com/compatible-mode/v1/chat/completions",
entity, String.class
);
return response.getBody();
}
}6. Common Mistakes and Troubleshooting
# | Symptom | Cause | Fix |
1 | Token consumption not attributed | x-dashscope-euid header is missing from the API call | Ensure the header is attached to every Bailian API request |
2 | Tracking is invalid | JSON field names use wrong casing (e.g., biztype instead of bizType) | Use exact camelCase for all JSON keys |
3 | Tracking is invalid | One or more of the 5 required fields are missing | Always include all 5 fields: bizType, moduleType, moduleCode, accountType, accountId |
4 | Tracking is invalid | moduleCode format is wrong | Must be market_${productCode} — do not forget the market_ prefix |
5 | Tokens attributed to wrong customer | accountId does not match the actual calling customer | Ensure your mapping table correctly binds each customer to their aliUid |
7. Best Practices
Centralize header construction. Build a single utility method that assembles the x-dashscope-euid header, so the logic is not duplicated across your codebase.
Use a JSON serialization library. Avoid hand-constructing JSON strings. Use your language's standard JSON library to ensure correct formatting.
Maintain a customer mapping table. Store customer → (productCode, aliUid) in your database, populated from SPI callbacks or the DescribeLicense API.
Add logging during development. Print the x-dashscope-euid value in debug logs so you can verify correctness before going live.
Test before production. Validate your tracking implementation in a staging environment, then ask your PDM or PSA to confirm the data is correctly reported on the backend.
8. Verification
After completing your implementation, contact your Alibaba Cloud PDM (Product Development Manager) or PSA (Partner Solution Architect) to:
Confirm that tracking data is being received on the platform side.
Verify that token consumption is correctly attributed to the right product and customer accounts.
Validate both the narrow-scope (marketplace customers) and broad-scope (non-marketplace fallback) paths.
9. Prerequisites and Constraints
Your product must call Bailian APIs using first-party models (e.g., Qwen, HappyHorse). Tracking only works for first-party model invocations via Bailian.
If your product currently uses models from other platforms or self-deployed models, you will need to migrate model calls to Bailian before tracking can be implemented.
Any programming language or SDK can be used, as long as you can attach custom HTTP headers to outbound requests.