Solution | Installation Required or Not | Applicable Scenarios |
axios (default example in this document) | npm install axios | Projects already use axios, or users prefer its API style. |
Node.js native fetch | No installation required (built into Node.js 18 or later) | Zero dependencies, recommended for modern projects. |
Node.js native https | No installation required | Compatible with older Node.js versions (< 18). |
npm install axios
// Replace the axios.post call// Original: const resp = await axios.post(`https://${MPS_HOST}`, payload, { headers });// return resp.data;// Replace with:const resp = await fetch(`https://${MPS_HOST}`, {method: 'POST',headers: headers,body: JSON.stringify(payload)});return await resp.json();
crypto module in Node.js can handle signature generation, so no additional installation is required. The signing process has no external dependencies.{"tencentCloud": {"secretId": "Your SecretId","secretKey": "Your SecretKey","region": "ap-guangzhou"}}
.gitignore).Core Capabilities | API | Action | Description | Concurrency Limit |
Text-to-3D | SubmitHunyuan3DTask QueryHunyuan3DTask | Generate a complete 3D model (geometry + texture) from a text Prompt. | 1 | |
Image-to-3D | | | Upload a reference image to generate the corresponding 3D model. | 1 |
Multiview-to-3D | | | Provide multi-view images (front/back/left/right) for the highest fidelity. | 1 |
/*** TencentCloud API signature tool (TC3-HMAC-SHA256)*/const crypto = require('crypto');function sha256(message) {return crypto.createHash('sha256').update(message).digest('hex');}function hmac256(key, message) {return crypto.createHmac('sha256', key).update(message).digest();}/*** Generate a TencentCloud API V3 signature.* @param {string} secretId - Tencent Cloud SecretId* @param {string} secretKey - Tencent Cloud SecretKey* @param {string} service - Service name, such as 'mps'* @param {string} action - API name, such as 'CreateAigcVideoTask'* @param {string} payload - Request body JSON string* @param {string} region - Region, such as 'ap-guangzhou'* @param {string} [version] - API version, default '2019-06-12'* @returns {{ headers: object }} - Request headers with the complete signature.*/function signRequest(secretId, secretKey, service, action, payload, region, version) {const timestamp = Math.floor(Date.now() / 1000);const date = new Date(timestamp * 1000).toISOString().split('T')[0];// ===== Step 1: Concatenate the canonical request string =====const httpRequestMethod = 'POST';const canonicalUri = '/';const canonicalQueryString = '';const contentType = 'application/json';const canonicalHeaders =`content-type:${contentType}\\n` +`host:${service}.tencentcloudapi.com\\n` +`x-tc-action:${action.toLowerCase()}\\n`;const signedHeaders = 'content-type;host;x-tc-action';const hashedRequestPayload = sha256(payload);const canonicalRequest =`${httpRequestMethod}\\n${canonicalUri}\\n${canonicalQueryString}\\n` +`${canonicalHeaders}\\n${signedHeaders}\\n${hashedRequestPayload}`;// ===== Step 2: Concatenate the string to sign =====const algorithm = 'TC3-HMAC-SHA256';const credentialScope = `${date}/${service}/tc3_request`;const hashedCanonicalRequest = sha256(canonicalRequest);const stringToSign =`${algorithm}\\n${timestamp}\\n${credentialScope}\\n${hashedCanonicalRequest}`;// ===== Step 3: Calculate the signature =====const secretDate = hmac256(`TC3${secretKey}`, date);const secretService = hmac256(secretDate, service);const secretSigning = hmac256(secretService, 'tc3_request');const signature = crypto.createHmac('sha256', secretSigning).update(stringToSign).digest('hex');// ===== Step 4: Concatenate the Authorization header =====const authorization =`${algorithm} Credential=${secretId}/${credentialScope}, ` +`SignedHeaders=${signedHeaders}, Signature=${signature}`;return {headers: {'Authorization': authorization,'Content-Type': contentType,'Host': `${service}.tencentcloudapi.com`,'X-TC-Action': action,'X-TC-Timestamp': String(timestamp),'X-TC-Version': version || '2019-06-12','X-TC-Region': region || ''}};}module.exports = { signRequest };
// mps-api.js — Hunyuan 3D Generation API wrapper (Node.js ≥ 18, native fetch, zero dependencies)const { signRequest } = require('./sign');const SERVICE = 'mps';const VERSION = '2019-06-12';const REGION = 'ap-guangzhou';const ENDPOINT = 'mps.intl.tencentcloudapi.com';const SECRET_ID = process.env.TENCENTCLOUD_SECRET_ID;const SECRET_KEY = process.env.TENCENTCLOUD_SECRET_KEY;/** Generic call: sign → send request → check for errors → return Response */async function callMpsApi(action, params) {const payload = params || {};const { authorization, timestamp } = signRequest({secretId: SECRET_ID,secretKey: SECRET_KEY,action,payload,});const res = await fetch('https://' + ENDPOINT + '/', {method: 'POST',headers: {Authorization: authorization,'Content-Type': 'application/json','X-TC-Action': action,'X-TC-Timestamp': String(timestamp),'X-TC-Version': VERSION,'X-TC-Region': REGION,},body: JSON.stringify(payload),});const data = await res.json();const r = data.Response || {};// Business error: check the standard error envelope Error.Code first, then the flat ErrorCode (such as ResourceNotFound.TaskId)if (r.Error || r.ErrorCode) {const code = (r.Error && r.Error.Code) || r.ErrorCode;const message = (r.Error && r.Error.Message) || r.ErrorMessage;throw new Error(`${action} failed: ${code} - ${message} (RequestId: ${r.RequestId})`);}return r;}/*** Submit a 3D generation task (text-to-3D / image-to-3D / multi-view-to-3D, with mutually exclusive input parameters)* @param {object} params* @param {string} [params.Prompt] Text-to-3D prompt, up to 1024 utf-8 characters.* @param {string} [params.ImageUrl] Image-to-3D reference image URL (jpg/jpeg/png/bmp/webp,* Short side ≥ 512, long side ≤ 4096, recommended ≤ 10 MB, and must be publicly accessible)* @param {Array<{ViewType: string, ViewImageUrl: string}>} [params.MultiViewImages]* Multi-view-to-3D: 2 to 8 images, must include the front view, and ViewType cannot be repeated.* Valid values: front / back / left / right / top / bottom / left_front / right_front* @param {string} [params.GenerateType='Normal'] Normal: complete 3D asset (geometry + texture);* Geometry: geometry only (faster, about 40s)* @param {boolean} [params.EnablePBR=false] Whether to output PBR materials.* @param {number} [params.FaceCount=500000] Number of faces, ranging from [3000, 1500000].* @returns {Promise<{TaskId: string, RequestId: string}>}* TaskId: unique task ID for subsequent queries. RequestId: request tracking ID. Provide this ID when locating issues.*/async function submitHunyuan3DTask(params) {return callMpsApi('SubmitHunyuan3DTask', params);}/*** Query a 3D generation task.* @param {string} taskId The task ID returned by Submit.* @returns {Promise<{* Status: 'WAIT' | 'RUN' | 'DONE' | 'FAIL',* Progress: number,* ErrorCode?: string, // Returned only when the status is FAIL, for example, InternalError.ModelInference.* ErrorMessage?: string, // Returned only when the status is FAIL.* ResultFile3Ds?: Array<{ // Returned only when the status is DONE.* Type: 'OBJ' | 'GLB' | 'MTL' | 'OBJ_ZIP',* Url: string,* PreviewImageUrl?: string* }>,* RequestId: string* }>}* ⚠️ The Url in ResultFile3Ds is a temporary signed URL with a validity period of about 24 hours. Download or transfer it promptly.* By default, OBJ + GLB formats are output. For OBJ, a standalone file and a ZIP package containing MTL+textures are also provided.*/async function queryHunyuan3DTask(taskId) {return callMpsApi('QueryHunyuan3DTask', { TaskId: taskId });}module.exports = { submitHunyuan3DTask, queryHunyuan3DTask };
const { submitHunyuan3DTask, queryHunyuan3DTask } = require('./mps-api');async function main() {// ① Submit a taskconst submitResp = await submitHunyuan3DTask({Prompt: 'a cute cartoon dinosaur, green, small horns',FaceCount: 500000,});const taskId = submitResp.TaskId;console.log('TaskId:', taskId);// ② Poll every 8 seconds.while (true) {const r = await queryHunyuan3DTask(taskId);console.log('Status:', r.Status, 'Progress:', r.Progress);if (r.Status === 'DONE') {// ⚠️ The following URLs are temporary signed URLs with a validity period of about 24 hours. Download or transfer them promptly.for (const f of r.ResultFile3Ds) {console.log(f.Type, '->', f.Url);}break;}if (r.Status === 'FAIL') {console.error('FAIL:', r.ErrorCode, r.ErrorMessage);break;}await new Promise(resolve => setTimeout(resolve, 8000));}}main().catch(console.error);
const { submitHunyuan3DTask } = require('./mps-api');// Scenario A: Image to 3D (full assets + PBR)await submitHunyuan3DTask({ImageUrl: 'https://example.com/test.png',EnablePBR: true,});// Scenario B: Image to 3D (geometry only, faster speed)await submitHunyuan3DTask({ImageUrl: 'https://example.com/test.png',GenerateType: 'Geometry',FaceCount: 100000,});// Scenario C: Multi-view image to 3D (at least 2 images, must include the front view; add left/right views for higher quality)await submitHunyuan3DTask({MultiViewImages: [{ ViewType: 'front', ViewImageUrl: 'https://example.com/front.png' },{ ViewType: 'back', ViewImageUrl: 'https://example.com/back.png' },],EnablePBR: true,});// Scenario D: Multi-view image to geometryawait submitHunyuan3DTask({MultiViewImages: [{ ViewType: 'front', ViewImageUrl: 'https://example.com/front.png' },{ ViewType: 'back', ViewImageUrl: 'https://example.com/back.png' },],GenerateType: 'Geometry',});
const { submitHunyuan3DTask, queryHunyuan3DTask } = require('./mps-api');/*** Generic polling function: with timeout and interval control* @param {string} taskId Task ID.* @param {object} [options]* @param {number} [options.timeoutMs=600000] Timeout in milliseconds. Defaults to 600000 (10 minutes).* @param {number} [options.intervalMs=8000] Polling interval in milliseconds. Defaults to 8000 (8 seconds). Do not set this value below 1000 (1 second), as it may trigger rate limiting.* @returns {Promise<object>} The complete Response in the DONE state.*/async function pollTaskResult(taskId, options = {}) {const { timeoutMs = 10 * 60 * 1000, intervalMs = 8000 } = options;const deadline = Date.now() + timeoutMs;while (Date.now() < deadline) {const r = await queryHunyuan3DTask(taskId);if (r.Status === 'DONE') return r;if (r.Status === 'FAIL') {throw new Error(`task failed: ${r.ErrorCode} - ${r.ErrorMessage}`);}await new Promise(resolve => setTimeout(resolve, intervalMs));}throw new Error(`poll timeout after ${timeoutMs} ms, TaskId: ${taskId}`);}/*** Production-grade workflow: Use Geometry mode to quickly validate the structure, and generate full assets once satisfied.* (Geometry only: about 40s. Full asset: about 120s. P50 reference values.)*/async function generateWithPreview(prompt) {// ① Run a geometry-only task first to quickly evaluate the model skeleton.const geometryTask = await submitHunyuan3DTask({Prompt: prompt,GenerateType: 'Geometry',FaceCount: 200000,});const geometryResult = await pollTaskResult(geometryTask.TaskId);console.log('Geometry preview completed. File count:', geometryResult.ResultFile3Ds.length);// ② Once satisfied, submit the full asset task (geometry + texture [+ PBR]).const finalTask = await submitHunyuan3DTask({Prompt: prompt,GenerateType: 'Normal',FaceCount: 500000,EnablePBR: true,});const finalResult = await pollTaskResult(finalTask.TaskId);// ⚠️ Download and transfer the file immediately after receiving DONE (URLs expire in about 24 hours).return finalResult.ResultFile3Ds; // List of OBJ / GLB files.}
{"tencentCloud": {"secretId": "TENCENTCLOUD_SECRET_ID","secretKey": "TENCENTCLOUD_SECRET_KEY","region": "ap-guangzhou"},"hunyuan3d": {"generateType": "Normal","enablePBR": false,"faceCount": 500000,"multiView": {"enabled": false,"minViews": 2,"maxViews": 8,"requiredViewType": "front"}},"concurrency": {"maxConcurrentTasks": 1,"pollIntervalSeconds": 8,"pollTimeoutMinutes": 10}}
Error Code | Description |
InvalidParameter.NoInputSpecified | None of Prompt / ImageUrl / MultiViewImages is passed. |
InvalidParameter.PromptImageConflict | Prompt and ImageUrl / MultiViewImages are provided at the same time. |
InvalidParameter.MultiInputConflict | ImageUrl and MultiViewImages are provided at the same time. |
InvalidParameter.MissingFrontView | MultiViewImages does not contain the front view. |
InvalidParameter.InsufficientViews | The number of MultiViewImages is less than 2. |
InvalidParameter.DuplicateViewType | Duplicate ViewType exists. |
InvalidParameter.FaceCountOutOfRange | FaceCount is not within [3000, 1500000]. |
InvalidParameter.PromptTooLong | Prompt exceeds 1024 utf-8 characters. |
Error Code | Reason |
AuthFailure.SignatureExpire | The deviation between the local time and Tencent CVM exceeds 5 minutes. Calibrate NTP. |
AuthFailure.SecretIdNotFound | SecretId does not exist or has been deleted. |
AuthFailure.UnauthorizedOperation / UnauthorizedOperation | The root account AppId is not yet in the Hunyuan 3D service allowlist (contact our business team to enable it), or the sub-account is not bound to the CAM policy QcloudMPSFullAccess. |
curl -X POST https://mps.intl.tencentcloudapi.com/ \\-H "Authorization: TC3-HMAC-SHA256 Credential=AKIDxxxxxxxx/2026-08-30/mps/tc3_request, SignedHeaders=content-type;host, Signature=fe5f6f..." \\-H "Content-Type: application/json" \\-H "X-TC-Action: SubmitHunyuan3DTask" \\-H "X-TC-Version: 2019-06-12" \\-H "X-TC-Timestamp: 1756543200" \\-H "X-TC-Region: ap-guangzhou" \\-d '{"Prompt": "a cute cartoon dinosaur, green, small horns","FaceCount": 500000}'
{"Response": {"TaskId": "r_44504e4b9a3b11f186b56a073b12405b","RequestId": "d344d00c-b131-45ec-8de9-829e0c427dca"}}
POST / HTTP/1.1Host: mps.intl.tencentcloudapi.comContent-Type: application/jsonX-TC-Action: QueryHunyuan3DTaskX-TC-Version: 2019-06-12{"TaskId": "r_44504e4b9a3b11f186b56a073b12405b"}
{"Response": {"Status": "DONE","Progress": 100,"ResultFile3Ds": [{"Type": "GLB","Url": "https://hunyuan-3d-12583xxxx3.cos.ap-guangzhou.myqcloud.com/gen_tmp_test/<task_hash>/<file_hash>.glb?q-sign-algorithm=sha1&...","PreviewImageUrl": "https://hunyuan-base-prod-12583xxxx3.cos.ap-guangzhou.myqcloud.com/openapi/text2img/<preview_hash>.png?q-sign-algorithm=sha1&..."},{"Type": "OBJ","Url": "https://hunyuan-3d-12583xxxx3.cos.ap-guangzhou.myqcloud.com/gen_tmp_test/<task_hash>/<file_hash>.obj?q-sign-algorithm=sha1&..."},{"Type": "OBJ","Url": "https://hunyuan-3d-12583xxxx3.cos.ap-guangzhou.myqcloud.com/gen_tmp_test/<task_hash>/<file_hash>.zip?q-sign-algorithm=sha1&..."}],"RequestId": "8189fa91-1c54-499c-8ce3-e182ec9c19ce"}}
{"Response": {"Status": "RUN","Progress": 0,"RequestId": "f3193547-4119-48d8-aa13-b27d29b49224"}}
{"Response": {"ErrorCode": "ResourceNotFound.TaskId","ErrorMessage": "task not found or expired","RequestId": "0ba287dc-f6b3-4f93-bc3a-887e7f971f90"}}
Was this page helpful?
You can also Contact sales or Submit a Ticket for help.
Help us improve! Rate your documentation experience in 5 mins.
Feedback