Developer API
Generate 3D models programmatically via the Trify3D REST API. Authenticate with API keys, run async jobs via Trigger.dev, and receive webhooks on completion.
https://trify3d.com/api/v1AuthBearer token (trf_live_…)FormatJSON request / responseScopesread, write, adminRate limit600 requests / hour / keyQuickstart
- Create an API key — sign in to the dashboard → API Keys → Create. Copy the plaintext value immediately; it is shown only once.
- Make your first request — generate a 3D model from text:
curl -X POST https://trify3d.com/api/v1/generations/text-to-3d \
-H "Authorization: Bearer trf_live_xxx" \
-H "Content-Type: application/json" \
-d '{
"type": "text_to_3d",
"prompt": "A medieval sword with golden hilt",
"style": "realistic",
"mode": "quality"
}'
# Response 201
{
"ok": true,
"data": {
"taskId": "gen_abc123",
"provider": "meshy",
"status": "processing",
"creditsUsed": 20,
"creditsRemaining": 980
},
"requestId": "req_1a2b3c4d"
}- Poll for completion —
GET https://trify3d.com/api/v1/generations/gen_abc123. Whenstatusiscompleted,data.outputModelUrlpoints to the generated.glb. - Receive a webhook (optional) — pass
webhookUrlon creation to be notified instead of polling.
Response envelope
All responses follow a single envelope. Every response carries an X-Request-ID header — include it when reporting issues.
// success (2xx)
{ "ok": true, "data": { /* … */ }, "requestId": "req_abc123" }
// error (4xx / 5xx)
{
"ok": false,
"error": {
"code": "snake_case_code",
"message": "Human-readable message.",
"requestId": "req_abc123",
"details": { /* optional, e.g. validation issues */ }
}
}Authentication
All /v1/* endpoints require a Bearer token in the Authorization header:
Authorization: Bearer trf_live_xxxxxxxxCreate keys from /api/api-keys using your session cookie. Keys are prefixed trf_live_ and stored as a SHA-256 hash; the plaintext is shown only once and is never retrievable after creation.
Managing keys
- Create — Dashboard → API Keys → Create. Plaintext shown once.
- Scopes — assigned at creation.
- Revoke — Dashboard → API Keys → Revoke. Revoked keys immediately return
401 invalid_api_key. - Rotation — revoke + create a new key. No grace period; update clients promptly.
Scopes
| Scope | Grants |
|---|---|
read | GET endpoints (e.g. fetch a task). |
write | POST endpoints (create generations). |
admin | All of the above (satisfies any requirement). |
A read-scoped key cannot create generations — the API returns 403 insufficient_scope.
Endpoints
/api/v1/generations/image-to-3dscope: writeTurn a single reference image (JPG/PNG) into a textured 3D model.
/api/v1/generations/text-to-3dscope: writeGenerate a 3D model from a natural-language prompt.
/api/v1/generations/multiview-to-3dscope: writeReconstruct a 3D model from 2+ photos of different angles.
/api/v1/generations/{taskId}scope: readPoll the status of a generation task. Returns status, output URL, and credit usage.
/api/api-keysscope: sessionList your API keys (requires session auth, not Bearer).
/api/api-keysscope: sessionCreate a new API key. Returns the plaintext key once.
/api/api-keys/{keyId}/revokescope: sessionRevoke an API key. Irreversible.
POST /generations/image-to-3d
Turn one reference image (JPG/PNG) into a textured 3D model. Upload the image as a base64 data URL.
curl -X POST https://trify3d.com/api/v1/generations/image-to-3d \
-H "Authorization: Bearer trf_live_xxx" \
-H "Content-Type: application/json" \
-d '{
"type": "image_to_3d",
"fileName": "sword.png",
"fileSize": 524288,
"fileType": "image/png",
"imageDataUrl": "data:image/png;base64,iVBORw0KGgo...",
"mode": "quality",
"model": "rodin/rodin-v25",
"multiPart": false,
"settings": {
"texture": true,
"hdTexture": true,
"geometryQuality": "standard",
"topology": "triangle"
},
"webhookUrl": "https://yourapp.com/hooks/trify3d"
}'
# 201 Created
{
"ok": true,
"data": {
"taskId": "gen_abc123",
"provider": "rodin",
"status": "processing",
"creditsUsed": 30,
"creditsRemaining": 970
},
"requestId": "req_1a2b3c4d"
}| Field | Required | Description |
|---|---|---|
type | yes | "image_to_3d" discriminator. |
fileName | yes | Original filename. |
fileSize | yes | Byte size. |
fileType | yes | MIME (image/jpeg, image/png). |
imageDataUrl | yes | Base64 data URL of the image. |
mode | yes | "speed" | "quality". |
model | no | Provider model id (e.g. rodin/rodin-v25). |
multiPart | no | Use multipart/rodin pipeline. |
settings | no | Texture / geometry options (see below). |
webhookUrl | no | HTTPS URL for completion callback. |
settings: texture (bool), hdTexture (bool), geometryQuality ("standard" | …), topology ("triangle" | …).
Provider selection: model starts with rodin/ → rodin; multiPart: true → rodin; mode: "speed" → tripo3d; otherwise meshy.
POST /generations/text-to-3d
Describe the model you want in natural language.
curl -X POST https://trify3d.com/api/v1/generations/text-to-3d \
-H "Authorization: Bearer trf_live_xxx" \
-H "Content-Type: application/json" \
-d '{
"type": "text_to_3d",
"prompt": "A medieval sword with golden hilt",
"style": "realistic",
"mode": "quality"
}'| Field | Required | Description |
|---|---|---|
type | yes | "text_to_3d" discriminator. |
prompt | yes | Natural-language description. |
style | yes | "realistic" | "cartoon" | "low_poly". |
mode | yes | "speed" | "quality". |
model | no | Provider model id. |
settings | no | Same shape as image-to-3d. |
webhookUrl | no | HTTPS URL for completion callback. |
POST /generations/multiview-to-3d
Reconstruct a 3D model from multiple photographs taken from different angles. Requires at least 2 images.
curl -X POST https://trify3d.com/api/v1/generations/multiview-to-3d \
-H "Authorization: Bearer trf_live_xxx" \
-H "Content-Type: application/json" \
-d '{
"type": "multiview_to_3d",
"imageDataUrls": [
"data:image/png;base64,iVBOR...",
"data:image/png;base64,iVBOR..."
],
"mode": "quality"
}'| Field | Required | Description |
|---|---|---|
type | yes | "multiview_to_3d" discriminator. |
imageDataUrls | yes | At least 2 base64 data URLs. |
mode | yes | "speed" | "quality". |
model | no | Provider model id. |
multiPart | no | Force rodin provider. |
settings | no | Same shape as image-to-3d. |
webhookUrl | no | HTTPS URL for completion callback. |
GET /generations/{taskId}
Returns the current state of a generation task. Tasks are scoped to the API key's account — a task created by another account's key returns 404.
curl https://trify3d.com/api/v1/generations/gen_abc123 \
-H "Authorization: Bearer trf_live_xxx"
# 200 OK (completed)
{
"ok": true,
"data": {
"taskId": "gen_abc123",
"type": "image_to_3d",
"status": "completed",
"provider": "meshy",
"outputModelUrl": "https://cdn.trify3d.com/models/gen_abc123/model.glb",
"thumbnailUrl": "https://cdn.trify3d.com/thumbnails/gen_abc123.png",
"creditsUsed": 20,
"errorCode": null,
"createdAt": "2026-06-22T13:55:00.000Z",
"completedAt": "2026-06-22T13:58:12.000Z"
},
"requestId": "req_3l4m5n6o"
}Status lifecycle: pending → processing → completed | failed. When failed, errorCode carries the reason.
Idempotency
Network retries are common. Without idempotency, a retried generation request could create a duplicate task and charge credits twice.
Pass an Idempotency-Key header on any POST endpoint. If the key is new, the request proceeds and credits are held against it. If the same key is submitted again, the credit hold is deduplicated — no double charge.
curl -X POST https://trify3d.com/api/v1/generations/text-to-3d \
-H "Authorization: Bearer trf_live_xxx" \
-H "Idempotency-Key: my-unique-key-123" \
-H "Content-Type: application/json" \
-d '{ ... }'Key format
- 1–255 characters.
- Allowed:
A–Z,a–z,0–9,_,-. - A random UUID v4 per client action is recommended.
Invalid keys return 400 invalid_idempotency_key. If the header is omitted, the server generates an internal key — server-side holds are still deduplicated, but client-driven retries will not benefit from cross-request deduplication.
Webhooks
If you pass webhookUrl on task creation, we POST the result when the job finishes (success or failure).
POST {your webhookUrl}
Headers:
X-Trify3D-Event: generation.completed # or generation.failed
X-Trify3D-Delivery: {taskId}
Body:
{
"taskId": "gen_abc123",
"type": "image_to_3d",
"status": "completed",
"provider": "meshy",
"outputModelUrl": "https://...",
"thumbnailUrl": "https://...",
"creditsUsed": 20,
"errorCode": null,
"timestamp": "2026-06-22T14:00:00.000Z"
}Delivery guarantees
- Retries — failed deliveries (non-2xx, network errors) retried up to 3 times with delays of 1s, 5s, 15s.
- 4xx (not 429) — treated as permanent failure, no retry.
- Timeout — requests abort after 10s.
Return any 2xx status to acknowledge. Keep your handler fast (< 10s) — offload heavy work to a queue.
Rate limiting
Each API key: 600 requests / hour, tracked in a rolling window. Responses include X-RateLimit-Limit, X-RateLimit-Remaining, and on 429 a Retry-After header (seconds).
| Header | Meaning |
|---|---|
X-RateLimit-Limit | Max requests in the window (600). |
X-RateLimit-Remaining | Requests remaining in the current window. |
Retry-After | Seconds until the window resets (429 only). |
X-Request-ID | Unique per-request ID for debugging. |
Errors
details is optional and carries structured context — e.g. validation_failed returns details.issues, insufficient_scope returns details.required / details.granted. Always include the requestId when contacting support.
| Code | HTTP | Meaning |
|---|---|---|
missing_bearer_token | 401 | No Authorization: Bearer … header. |
invalid_api_key | 401 | Key is malformed, revoked, or unknown. |
insufficient_scope | 403 | Key lacks the scope for this endpoint. See details.required. |
insufficient_credits | 402 | Account balance too low for this generation. |
validation_failed | 400 | Request body failed validation. See details.issues. |
invalid_idempotency_key | 400 | Idempotency-Key header format invalid. |
invalid_request_body | 400 | Request body is not valid JSON. |
rate_limit_exceeded | 429 | Hourly quota exhausted. See Retry-After. |
task_not_found | 404 | No task with that ID for this account. |
internal_error | 500 | Unexpected server error. Retry with backoff; report requestId. |
Validation issue codes
| Code | Meaning |
|---|---|
prompt_required | Empty prompt (text-to-3d). |
prompt_too_long | Prompt exceeds the max length. |
image_required | Missing reference image, or fewer than 2 images (multiview). |
image_type_unsupported | File type not in the supported set. |
image_too_large | File exceeds the size limit. |
Handling errors
- 4xx (except 429) — do not retry blindly; fix the request.
- 429 — honor
Retry-After, then retry. - 5xx — retry with exponential backoff (1s, 2s, 4s, 8s).
- Always log
requestIdalongside the error for traceability.
Async model
Jobs run on Trigger.dev with retries and 10-minute max duration. Poll GET /api/v1/generations/{taskId} or wait for the webhook — your choice.