> For the complete documentation index, see [llms.txt](https://docs.m-xr.com/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.m-xr.com/marso-studio-api/api/pbr.md).

# PBR

Submit meshes and images for PBR generation.

Batches group one or more PBR generation jobs together. Use these endpoints to submit 3D mesh to PBR or image to PBR batches, poll a batch for progress, and list recent batches.

## Generate PBR materials from 3D meshes.

> Submits one or more ready mesh assets for 3D mesh to PBR generation. The same\
> \`options\` are applied to every mesh in the batch. Each asset must be a\
> \`ready\` mesh uploaded via \`POST /batch/uploads\`.\
> \
> The request returns immediately with a \`batch\_id\` (\`202\`). Poll\
> \`GET /batch/{batch\_id}\` for the batch status and, as each item finishes,\
> its generated material maps and download URLs. Credits are reserved when\
> you submit and refunded automatically for any item that fails, so you\
> are only charged for successful results.<br>

```json
{"openapi":"3.1.0","info":{"title":"Marso Studio API","version":"0.0.1"},"tags":[{"name":"pbr","description":"Batches group one or more PBR generation jobs together. Use these endpoints\nto submit 3D mesh to PBR or image to PBR batches, poll a batch for progress,\nand list recent batches.\n"}],"servers":[{"url":"https://api.marso.app/api/public/v1/","description":"Production server."}],"security":[{"apiKey":[]}],"components":{"securitySchemes":{"apiKey":{"type":"http","scheme":"bearer","description":"API key created in the Marso Studio dashboard. The value has the form\n`mxr_live_<entropy>` and is sent as a Bearer token in the `Authorization`\nheader.\n"}},"schemas":{"CreatePBRMeshBatchRequest":{"type":"object","required":["asset_ids"],"description":"Request body for `POST /batch/pbr/mesh`. Submit one or more ready mesh\nassets for 3D mesh to PBR generation.\n","properties":{"asset_ids":{"type":"array","minItems":1,"maxItems":200,"items":{"type":"string","format":"uuid"},"description":"IDs of the `ready` mesh assets to process. These assets must have been\nuploaded via `POST /batch/uploads` and must currently be in the `ready`\nstate.\n"},"options":{"$ref":"#/components/schemas/PublicPBRMeshOptions"}}},"PublicPBRMeshOptions":{"type":"object","additionalProperties":false,"description":"Tuning options for a mesh PBR run. Every field is optional, and an\nomitted field uses the system default.\n\nMesh options include `detail_transfer` and `output_2d`. They do not\ninclude `tileable`. Every mesh result includes a USDZ file. Unknown\nfields return a `400` response.\n","properties":{"detail_transfer":{"$ref":"#/components/schemas/PublicPBRDetailTransfer"},"output_2d":{"$ref":"#/components/schemas/PublicPBROutput2D"}}},"PublicPBRDetailTransfer":{"type":"object","additionalProperties":false,"description":"Settings for detail transfer during PBR generation.\n","properties":{"enabled":{"type":["boolean","null"],"default":true,"description":"Whether detail transfer is enabled. An omitted field, and an\nexplicit `null`, both enable detail transfer.\n"}}},"PublicPBROutput2D":{"type":"object","additionalProperties":false,"description":"Options for 2D output. All fields are optional; omitted fields\nuse system defaults.\n","properties":{"orm":{"$ref":"#/components/schemas/PublicPBROutput2DORM"}}},"PublicPBROutput2DORM":{"type":"object","additionalProperties":false,"description":"Settings for an occlusion/roughness/metallic (ORM) combined 2D output\nmap.\n\nThe API accepts this option, but it currently has no effect. Results do\nnot include an ORM map.\n","properties":{"enabled":{"type":["boolean","null"],"default":false,"description":"Whether to request the ORM map. An omitted field and an explicit\n`null` both use `false`. This option currently has no effect.\n"},"channel_order":{"type":"string","enum":["ORM","RMO","MRO"],"default":"ORM","description":"Channel order for the combined map. An omitted field packs the\nchannels in the `ORM` order.\n\nPossible enum values:\n- `ORM`: occlusion, roughness, metallic.\n- `RMO`: roughness, metallic, occlusion.\n- `MRO`: metallic, roughness, occlusion.\n"}}},"BatchResponse":{"type":"object","required":["batch_id","status","request_counts","created_at","items"],"description":"Body returned by `POST /batch/pbr/*` (202) and `GET /batch/{batch_id}`\n(200). On submission `items` is empty; poll the batch to populate it.\n`completed_at` is set once the batch reaches a terminal status; `failed_at`\nis set only when that terminal status is `FAILED`.\n","properties":{"batch_id":{"type":"string","description":"Unique identifier of the batch.\n"},"status":{"$ref":"#/components/schemas/BatchStatus"},"request_counts":{"$ref":"#/components/schemas/BatchCountsBody"},"created_at":{"$ref":"#/components/schemas/EpochMillis"},"completed_at":{"$ref":"#/components/schemas/NullableEpochMillis"},"failed_at":{"$ref":"#/components/schemas/NullableEpochMillis"},"items":{"type":"array","description":"Finished results for each item. Empty until at least one item has\nfinished.\n","items":{"$ref":"#/components/schemas/BatchItemBody"}}}},"BatchStatus":{"type":"string","description":"Aggregate status of a PBR batch.\n\nPossible enum values:\n- `QUEUED`: the batch is waiting to start.\n- `RUNNING`: at least one item is being processed.\n- `COMPLETED`: every item succeeded.\n- `PARTIALLY_COMPLETED`: the batch finished with a mix of successes and\n  failures.\n- `FAILED`: every item failed.\n","enum":["QUEUED","RUNNING","COMPLETED","PARTIALLY_COMPLETED","FAILED"]},"BatchCountsBody":{"type":"object","required":["total","completed","failed","pending"],"description":"Count of items in a batch, grouped by status.\n","properties":{"total":{"type":"integer","description":"Total number of items in the batch.\n"},"completed":{"type":"integer","description":"Number of items that have reached a terminal state, whether they\nsucceeded or failed.\n"},"failed":{"type":"integer","description":"Number of items that failed.\n"},"pending":{"type":"integer","description":"Number of items that are still queued or running.\n"}}},"EpochMillis":{"type":"integer","format":"int64","description":"Time represented as milliseconds since the Unix epoch.\n"},"NullableEpochMillis":{"type":["integer","null"],"format":"int64","description":"Time represented as milliseconds since the Unix epoch, or `null` when the\nfield has not been set.\n"},"BatchItemBody":{"type":"object","required":["execution_id","status","created_at"],"description":"One item's result within a batch. `asset_id` is the source asset you\nsubmitted, so a failed item is always traceable to its input.\n`pbr_material_assets` maps each `\"<slot>/<pass>\"` to its generated texture\nasset and a ready-to-use `download_url`, so you can pull every result\ndirectly from the poll response. `usdz` is the textured 3D model of a\nsucceeded mesh item, in the same shape. `thumbnail_url` is a preview\nimage. It is always present on a succeeded mesh item, and best-effort on\na succeeded image item: such an item may finish without one. A failed\nitem carries `error`, and no other item does.\n","properties":{"execution_id":{"type":"string","description":"Unique identifier of the execution for this item.\n"},"asset_id":{"type":"string","description":"ID of the source asset that was submitted for this item.\n"},"status":{"$ref":"#/components/schemas/ExecutionStatus"},"thumbnail_url":{"type":"string","format":"uri","description":"URL of a preview image. Always present on a succeeded mesh item;\nbest-effort on a succeeded image item.\n"},"thumbnail_url_expires_at":{"$ref":"#/components/schemas/EpochMillis"},"pbr_material_assets":{"type":"object","description":"Map from `\"<slot>/<pass>\"` to the generated texture asset. Only\npresent on succeeded items. `\"<slot>/normal\"` is an example of a key.\nThe map can hold other keys, and a key can be absent. Check that a key\nexists before you read it. When a 3D mesh to PBR job returns a normal\nmap, the map uses OpenGL tangent space. Do not assume a space for the\nnormal map of an image to PBR job.\n","additionalProperties":{"$ref":"#/components/schemas/BatchMaterialAssetBody"}},"usdz":{"allOf":[{"$ref":"#/components/schemas/BatchMaterialAssetBody"}],"description":"The `material.usdz` of the item: one self-contained file that carries\nthe 3D model and its generated textures. A succeeded mesh item\ncarries it, and an image item carries none. An item that finished\nsome time ago may carry none.\n"},"error":{"$ref":"#/components/schemas/ExecutionErrorBody"},"created_at":{"$ref":"#/components/schemas/EpochMillis"}}},"ExecutionStatus":{"type":"string","description":"Status of a single item within a batch.\n\nPossible enum values:\n- `QUEUED`: the item is waiting to start.\n- `RUNNING`: the item is being processed.\n- `SUCCEEDED`: the item completed successfully.\n- `PARTIAL_SUCCEEDED`: the item completed but some optional outputs are\n  missing.\n- `FAILED`: the item failed. See the `error` field for details.\n- `CANCELED`: the item stopped without producing a result, and any\n  credits held for it are returned. Submit the request again to retry\n  it.\n","enum":["QUEUED","RUNNING","SUCCEEDED","PARTIAL_SUCCEEDED","FAILED","CANCELED"]},"BatchMaterialAssetBody":{"type":"object","required":["asset_id"],"description":"One generated PBR result asset within a batch item. `download_url` is a\nready-to-use link that expires at `expires_at`.\n","properties":{"asset_id":{"type":"string","description":"ID of the generated asset.\n"},"download_url":{"type":"string","format":"uri","description":"Presigned URL for downloading the generated asset. Valid until\n`expires_at`.\n"},"expires_at":{"$ref":"#/components/schemas/EpochMillis"}}},"ExecutionErrorBody":{"type":"object","description":"Failure detail for a failed batch item, and for a failed run that\n`GET /mcp/executions/{execution_id}` reads. The `code` is a stable,\nmachine-readable class drawn from a closed enum so clients can branch on\nthe failure kind. The `message` carries human-readable detail. Only\n`VALIDATION_FAILED` is permanent; do not retry the same input. The other\ncodes are generally retryable.\n","required":["code","message"],"properties":{"code":{"type":"string","enum":["VALIDATION_FAILED","PIPELINE_ERROR","TIMEOUT","INTERNAL"],"description":"Machine-readable class of the failure.\n\nPossible enum values:\n- `VALIDATION_FAILED`: the input is unsuitable or invalid. This is a\n  permanent failure; retrying the same input will not succeed. It\n  covers a failure that you can fix with a change of the input. For\n  example, a 3D asset edit of a model that the edit does not support\n  yet, such as a model with 2 or more colour textures, fails with this\n  code and a message that names the cause. An upload status read uses\n  the same word for a file that the API could not check. That case is\n  retryable (see `ErrorCode`).\n- `PIPELINE_ERROR`: PBR generation started but failed.\n  Generally retryable.\n- `TIMEOUT`: the item exceeded its time budget. Generally retryable.\n- `INTERNAL`: an unexpected server error. Generally retryable.\n"},"message":{"type":"string","description":"Human-readable explanation of the failure. The message can include\nnames from the file that you uploaded, such as a material name.\n"}}},"Error":{"type":"object","required":["error"],"description":"Canonical error envelope. The error payload is nested under the `error`\nkey.\n","properties":{"error":{"$ref":"#/components/schemas/ErrorBody"}}},"ErrorBody":{"type":"object","description":"The body of an error response. The `code` is a stable value clients can\nbranch on; the `message` is a human-readable explanation.\n","required":["code","message"],"properties":{"code":{"$ref":"#/components/schemas/ErrorCode"},"message":{"type":"string","description":"Human-readable description of the error.\n"},"details":{"type":["object","null"],"description":"Additional structured information about the error, or `null` if no\nextra details are available.\n","additionalProperties":true}}},"ErrorCode":{"type":"string","description":"Machine-readable error code. The enum contains every code that this API\ncan return.\n\nPossible enum values:\n- `INVALID_REQUEST`: the request body or a query parameter failed validation.\n- `MALFORMED_UUID`: a path parameter that should be a UUID is not one.\n- `UNAUTHORIZED`: the request could not be authenticated.\n- `API_KEY_MISSING`: no API key was supplied.\n- `API_KEY_INVALID`: the supplied API key does not match any known key.\n- `API_KEY_REVOKED`: the supplied API key has been revoked.\n- `PLAN_FEATURE_REQUIRED`: the account plan does not include a required feature.\n- `SUBSCRIPTION_FEATURE_REQUIRED`: a new Marketplace purchase requires a paid plan or assigned seat.\n- `PRICE_CHANGED`: the selected price differs from the price in the request.\n- `VERSION_CHANGED`: the selected Marketplace version differs from the requested version.\n- `IDEMPOTENCY_CONFLICT`: the same key was used for a different purchase request.\n- `DOWNLOAD_UNAVAILABLE`: the retained private content is temporarily unavailable.\n- `CATALOGUE_CHANGED`: the catalogue changed during pagination; restart the query.\n- `LISTING_NOT_FOUND`: the selected listing is absent or delisted.\n- `MARKETPLACE_DOWNLOAD_ONLY`: Marketplace content supports download and cannot enter generation.\n- `INSUFFICIENT_CREDITS`: the account does not have enough credits for this request.\n- `QUOTA_EXCEEDED`: a usage quota for the account has been exceeded.\n- `FILE_TOO_LARGE`: an uploaded or referenced file exceeds the allowed size.\n- `UPLOAD_TIMEOUT`: an image search upload exceeded 60 seconds. Try a smaller file or a faster connection.\n- `INVALID_CONTENT_TYPE`: a file's content type does not match its declared or expected type.\n- `UNSUPPORTED_ASSET_FORMAT`: a file's extension is not one of the supported asset formats.\n- `FORMAT_NOT_AVAILABLE`: the requested downloadable format does not exist for this asset.\n- `INVALID_FBX_ZIP_STRUCTURE`: a `.zip` upload is not a valid FBX bundle. The error message says why.\n- `IMAGE_RESOLUTION_EXCEEDED`: an image is larger than 4096 by 4096 pixels, or a texture in a mesh file is larger than 8192 by 8192 pixels.\n- `VALIDATION_FAILED`: the API could not check the uploaded file. Upload it again. This is the `error.code` of an upload status read. A failed batch item or run uses the same word with another meaning (see `ExecutionErrorBody`).\n- `UPLOAD_EXPIRED`: the upload did not finish within 1 hour. Upload the file again.\n- `MESH_REQUIREMENTS_NOT_MET`: the mesh file has no polygon geometry, no base-color texture, no UV coordinates, or it names a texture that the upload does not hold.\n- `COLLECTION_NOT_FOUND`: the collection does not exist in your account.\n- `PROJECT_NOT_FOUND`: the project does not exist, or another account owns it.\n- `GRAPH_NOT_FOUND`: the graph does not exist in this project.\n- `NODE_NOT_FOUND`: the node does not exist in this graph.\n- `GROUP_NOT_FOUND`: the group does not exist in this graph.\n- `PROJECT_NODE_LIMIT_EXCEEDED`: the project already holds as many nodes as it may hold.\n- `CONFLICT`: the request conflicts with the current state, so the server made no change.\n- `ASSET_NOT_FOUND`: the asset does not exist or does not belong to the account.\n- `ASSET_IN_USE`: an active execution uses a component, or another PBR execution shares a source or target component.\n- `BATCH_TOO_LARGE`: the batch contains more items than the endpoint allows.\n- `CONCURRENT_EXECUTION_LIMIT_EXCEEDED`: the account has reached its limit on simultaneous submissions.\n- `PBR_FAILURE_CAP_EXCEEDED`: the account has too many recent failed PBR runs; new submissions are temporarily blocked.\n- `EXECUTION_NOT_FOUND`: the execution or batch does not exist or does not belong to the account.\n- `EXECUTION_IN_PROGRESS`: the request conflicts with an execution that is still running.\n- `RATE_LIMITED`: the API key's rate limit was exceeded.\n- `SERVICE_UNAVAILABLE`: the requested operation is temporarily unavailable; retry later.\n- `MAINTENANCE_MODE`: the API is temporarily unavailable for maintenance.\n- `INTERNAL_SERVER_ERROR`: an unexpected server error occurred.\n- `WORKFLOW_CREATION_FAILED`: the API could not start the requested generation.\n- `WORKFLOW_API_ERROR`: generation stopped because of an unexpected error.\n","enum":["INVALID_REQUEST","MALFORMED_UUID","UNAUTHORIZED","API_KEY_MISSING","API_KEY_INVALID","API_KEY_REVOKED","PLAN_FEATURE_REQUIRED","SUBSCRIPTION_FEATURE_REQUIRED","PRICE_CHANGED","VERSION_CHANGED","IDEMPOTENCY_CONFLICT","DOWNLOAD_UNAVAILABLE","CATALOGUE_CHANGED","LISTING_NOT_FOUND","MARKETPLACE_DOWNLOAD_ONLY","INSUFFICIENT_CREDITS","QUOTA_EXCEEDED","FILE_TOO_LARGE","UPLOAD_TIMEOUT","INVALID_CONTENT_TYPE","UNSUPPORTED_ASSET_FORMAT","FORMAT_NOT_AVAILABLE","INVALID_FBX_ZIP_STRUCTURE","IMAGE_RESOLUTION_EXCEEDED","VALIDATION_FAILED","UPLOAD_EXPIRED","MESH_REQUIREMENTS_NOT_MET","COLLECTION_NOT_FOUND","PROJECT_NOT_FOUND","GRAPH_NOT_FOUND","NODE_NOT_FOUND","GROUP_NOT_FOUND","PROJECT_NODE_LIMIT_EXCEEDED","CONFLICT","ASSET_NOT_FOUND","ASSET_IN_USE","BATCH_TOO_LARGE","CONCURRENT_EXECUTION_LIMIT_EXCEEDED","PBR_FAILURE_CAP_EXCEEDED","EXECUTION_NOT_FOUND","EXECUTION_IN_PROGRESS","RATE_LIMITED","SERVICE_UNAVAILABLE","MAINTENANCE_MODE","INTERNAL_SERVER_ERROR","WORKFLOW_CREATION_FAILED","WORKFLOW_API_ERROR"]},"PlanFeatureRequiredError":{"type":"object","required":["error"],"description":"The error envelope for a missing subscription feature.","properties":{"error":{"type":"object","required":["code","message","details"],"description":"The missing-feature error body.","properties":{"code":{"type":"string","const":"PLAN_FEATURE_REQUIRED","description":"Stable error code for a missing subscription feature."},"message":{"type":"string","description":"Human-readable description of the error."},"details":{"$ref":"#/components/schemas/PlanFeatureRequiredErrorDetails"}}}}},"PlanFeatureRequiredErrorDetails":{"type":"object","required":["feature","upgrade_plan_codes"],"description":"The missing feature and the active plans that provide it.","properties":{"feature":{"$ref":"#/components/schemas/SubscriptionFeature"},"upgrade_plan_codes":{"type":"array","description":"Active plans available for purchase that provide the missing\nfeature. The array uses plan class and opaque code order.\n","items":{"$ref":"#/components/schemas/SubscriptionPlanCode"}}}},"SubscriptionFeature":{"type":"string","description":"Product capability available to the account.\n\nPossible enum values:\n- `commercial_license`: commercial-use rights.\n- `api_pbr`: PBR generation through the Public Platform API.\n- `api_asset_download`: asset downloads through the Public Platform API.\n- `custom_polycount`: custom polygon counts for 3D generation.\n- `image_edit`: image editing.\n- `multiview`: multiview generation.\n- `asset_download`: asset downloads in the Marso web app.\n- `api_access`: access to the Public Platform API.\n- `scene_generation`: scene generation and multi-asset runs in the Marso web app.\n- `application_apps`: the Sim Ready and Game Ready apps.\n","enum":["commercial_license","api_pbr","api_asset_download","custom_polycount","image_edit","multiview","asset_download","api_access","scene_generation","application_apps"]},"SubscriptionPlanCode":{"type":"string","pattern":"^plan_[A-Za-z0-9]{7,64}$","description":"Opaque and immutable code for a subscription plan. A plan display name\ncan change. The code starts with `plan_`, followed by 7 to 64 ASCII\nletters or digits.\n"}},"responses":{"Unauthorized":{"description":"The API key is missing, invalid, or revoked. Possible error codes:\n`API_KEY_MISSING`, `API_KEY_INVALID`, `API_KEY_REVOKED`, or `UNAUTHORIZED`.\n","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"PlanFeatureRequired":{"description":"The account does not have the plan feature required by this operation\n(`PLAN_FEATURE_REQUIRED`). The Free plan lacks `api_access`, so every\noperation refuses a Free account with this response. An account that\nlacks `api_pbr` or `api_asset_download` also gets it on the routes that\nneed those features.\n","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlanFeatureRequiredError"}}}},"RateLimitedOrCapped":{"description":"The request could not be accepted because of a rate or concurrency limit.\nPossible error codes:\n\n- `RATE_LIMITED`: the API-key rate limit was exceeded.\n- `CONCURRENT_EXECUTION_LIMIT_EXCEEDED`: a single submission exceeded\n  the concurrency limit.\n- `PBR_FAILURE_CAP_EXCEEDED`: too many consecutive failures; check the\n  input before retrying.\n","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"paths":{"/batch/pbr/mesh":{"post":{"operationId":"createMeshPBRBatch","tags":["pbr"],"summary":"Generate PBR materials from 3D meshes.","description":"Submits one or more ready mesh assets for 3D mesh to PBR generation. The same\n`options` are applied to every mesh in the batch. Each asset must be a\n`ready` mesh uploaded via `POST /batch/uploads`.\n\nThe request returns immediately with a `batch_id` (`202`). Poll\n`GET /batch/{batch_id}` for the batch status and, as each item finishes,\nits generated material maps and download URLs. Credits are reserved when\nyou submit and refunded automatically for any item that fails, so you\nare only charged for successful results.\n","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreatePBRMeshBatchRequest"}}}},"responses":{"202":{"description":"Batch created and queued.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BatchResponse"}}}},"400":{"description":"The request is invalid because the `asset_ids` list is empty or too\nlong, an asset was not found or is not owned by the authenticated account, an asset\nis not a mesh, or an asset is not `ready` (`INVALID_REQUEST`).\n","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/PlanFeatureRequired"},"429":{"$ref":"#/components/responses/RateLimitedOrCapped"}}}}}}
```

## Generate PBR materials from images.

> Submits one or more ready image assets for image to PBR generation. The\
> image counterpart of \`POST /batch/pbr/mesh\`. It takes \`detail\_transfer\`\
> and the image-only \`tileable\` option, but not \`output\_2d\`. Image results\
> do not include a USDZ file.\
> \
> The request returns immediately with a \`batch\_id\` (\`202\`). Poll\
> \`GET /batch/{batch\_id}\` for the batch status and each image's generated\
> material maps and download URLs. Credits are reserved when you submit\
> and refunded automatically for any item that fails.<br>

```json
{"openapi":"3.1.0","info":{"title":"Marso Studio API","version":"0.0.1"},"tags":[{"name":"pbr","description":"Batches group one or more PBR generation jobs together. Use these endpoints\nto submit 3D mesh to PBR or image to PBR batches, poll a batch for progress,\nand list recent batches.\n"}],"servers":[{"url":"https://api.marso.app/api/public/v1/","description":"Production server."}],"security":[{"apiKey":[]}],"components":{"securitySchemes":{"apiKey":{"type":"http","scheme":"bearer","description":"API key created in the Marso Studio dashboard. The value has the form\n`mxr_live_<entropy>` and is sent as a Bearer token in the `Authorization`\nheader.\n"}},"schemas":{"CreatePBRImageBatchRequest":{"type":"object","required":["asset_ids"],"description":"Request body for `POST /batch/pbr/image`. Image PBR options include\n`tileable`, and do not include `output_2d`.\n","properties":{"asset_ids":{"type":"array","minItems":1,"maxItems":200,"items":{"type":"string","format":"uuid"},"description":"IDs of the `ready` image assets to process. These assets must have\nbeen uploaded via `POST /batch/uploads` and must currently be in the\n`ready` state.\n"},"options":{"$ref":"#/components/schemas/PublicPBRImageOptions"}}},"PublicPBRImageOptions":{"type":"object","additionalProperties":false,"description":"Tuning options for an image PBR run. Every field is optional, and an\nomitted field uses the system default.\n\nImage options include `detail_transfer` and `tileable`. Image results\ndo not include a USDZ file. Unknown fields return a `400` response.\n","properties":{"detail_transfer":{"$ref":"#/components/schemas/PublicPBRDetailTransfer"},"tileable":{"type":["boolean","null"],"default":false,"description":"Whether the image is a seamless texture, one that repeats with no\nvisible seam when you tile it. Set it to `true` only for such a\ntexture, not for a photograph. The generated maps then also tile\nwithout a seam at their edges. An omitted field and an explicit\n`null` both use `false`.\n"}}},"PublicPBRDetailTransfer":{"type":"object","additionalProperties":false,"description":"Settings for detail transfer during PBR generation.\n","properties":{"enabled":{"type":["boolean","null"],"default":true,"description":"Whether detail transfer is enabled. An omitted field, and an\nexplicit `null`, both enable detail transfer.\n"}}},"BatchResponse":{"type":"object","required":["batch_id","status","request_counts","created_at","items"],"description":"Body returned by `POST /batch/pbr/*` (202) and `GET /batch/{batch_id}`\n(200). On submission `items` is empty; poll the batch to populate it.\n`completed_at` is set once the batch reaches a terminal status; `failed_at`\nis set only when that terminal status is `FAILED`.\n","properties":{"batch_id":{"type":"string","description":"Unique identifier of the batch.\n"},"status":{"$ref":"#/components/schemas/BatchStatus"},"request_counts":{"$ref":"#/components/schemas/BatchCountsBody"},"created_at":{"$ref":"#/components/schemas/EpochMillis"},"completed_at":{"$ref":"#/components/schemas/NullableEpochMillis"},"failed_at":{"$ref":"#/components/schemas/NullableEpochMillis"},"items":{"type":"array","description":"Finished results for each item. Empty until at least one item has\nfinished.\n","items":{"$ref":"#/components/schemas/BatchItemBody"}}}},"BatchStatus":{"type":"string","description":"Aggregate status of a PBR batch.\n\nPossible enum values:\n- `QUEUED`: the batch is waiting to start.\n- `RUNNING`: at least one item is being processed.\n- `COMPLETED`: every item succeeded.\n- `PARTIALLY_COMPLETED`: the batch finished with a mix of successes and\n  failures.\n- `FAILED`: every item failed.\n","enum":["QUEUED","RUNNING","COMPLETED","PARTIALLY_COMPLETED","FAILED"]},"BatchCountsBody":{"type":"object","required":["total","completed","failed","pending"],"description":"Count of items in a batch, grouped by status.\n","properties":{"total":{"type":"integer","description":"Total number of items in the batch.\n"},"completed":{"type":"integer","description":"Number of items that have reached a terminal state, whether they\nsucceeded or failed.\n"},"failed":{"type":"integer","description":"Number of items that failed.\n"},"pending":{"type":"integer","description":"Number of items that are still queued or running.\n"}}},"EpochMillis":{"type":"integer","format":"int64","description":"Time represented as milliseconds since the Unix epoch.\n"},"NullableEpochMillis":{"type":["integer","null"],"format":"int64","description":"Time represented as milliseconds since the Unix epoch, or `null` when the\nfield has not been set.\n"},"BatchItemBody":{"type":"object","required":["execution_id","status","created_at"],"description":"One item's result within a batch. `asset_id` is the source asset you\nsubmitted, so a failed item is always traceable to its input.\n`pbr_material_assets` maps each `\"<slot>/<pass>\"` to its generated texture\nasset and a ready-to-use `download_url`, so you can pull every result\ndirectly from the poll response. `usdz` is the textured 3D model of a\nsucceeded mesh item, in the same shape. `thumbnail_url` is a preview\nimage. It is always present on a succeeded mesh item, and best-effort on\na succeeded image item: such an item may finish without one. A failed\nitem carries `error`, and no other item does.\n","properties":{"execution_id":{"type":"string","description":"Unique identifier of the execution for this item.\n"},"asset_id":{"type":"string","description":"ID of the source asset that was submitted for this item.\n"},"status":{"$ref":"#/components/schemas/ExecutionStatus"},"thumbnail_url":{"type":"string","format":"uri","description":"URL of a preview image. Always present on a succeeded mesh item;\nbest-effort on a succeeded image item.\n"},"thumbnail_url_expires_at":{"$ref":"#/components/schemas/EpochMillis"},"pbr_material_assets":{"type":"object","description":"Map from `\"<slot>/<pass>\"` to the generated texture asset. Only\npresent on succeeded items. `\"<slot>/normal\"` is an example of a key.\nThe map can hold other keys, and a key can be absent. Check that a key\nexists before you read it. When a 3D mesh to PBR job returns a normal\nmap, the map uses OpenGL tangent space. Do not assume a space for the\nnormal map of an image to PBR job.\n","additionalProperties":{"$ref":"#/components/schemas/BatchMaterialAssetBody"}},"usdz":{"allOf":[{"$ref":"#/components/schemas/BatchMaterialAssetBody"}],"description":"The `material.usdz` of the item: one self-contained file that carries\nthe 3D model and its generated textures. A succeeded mesh item\ncarries it, and an image item carries none. An item that finished\nsome time ago may carry none.\n"},"error":{"$ref":"#/components/schemas/ExecutionErrorBody"},"created_at":{"$ref":"#/components/schemas/EpochMillis"}}},"ExecutionStatus":{"type":"string","description":"Status of a single item within a batch.\n\nPossible enum values:\n- `QUEUED`: the item is waiting to start.\n- `RUNNING`: the item is being processed.\n- `SUCCEEDED`: the item completed successfully.\n- `PARTIAL_SUCCEEDED`: the item completed but some optional outputs are\n  missing.\n- `FAILED`: the item failed. See the `error` field for details.\n- `CANCELED`: the item stopped without producing a result, and any\n  credits held for it are returned. Submit the request again to retry\n  it.\n","enum":["QUEUED","RUNNING","SUCCEEDED","PARTIAL_SUCCEEDED","FAILED","CANCELED"]},"BatchMaterialAssetBody":{"type":"object","required":["asset_id"],"description":"One generated PBR result asset within a batch item. `download_url` is a\nready-to-use link that expires at `expires_at`.\n","properties":{"asset_id":{"type":"string","description":"ID of the generated asset.\n"},"download_url":{"type":"string","format":"uri","description":"Presigned URL for downloading the generated asset. Valid until\n`expires_at`.\n"},"expires_at":{"$ref":"#/components/schemas/EpochMillis"}}},"ExecutionErrorBody":{"type":"object","description":"Failure detail for a failed batch item, and for a failed run that\n`GET /mcp/executions/{execution_id}` reads. The `code` is a stable,\nmachine-readable class drawn from a closed enum so clients can branch on\nthe failure kind. The `message` carries human-readable detail. Only\n`VALIDATION_FAILED` is permanent; do not retry the same input. The other\ncodes are generally retryable.\n","required":["code","message"],"properties":{"code":{"type":"string","enum":["VALIDATION_FAILED","PIPELINE_ERROR","TIMEOUT","INTERNAL"],"description":"Machine-readable class of the failure.\n\nPossible enum values:\n- `VALIDATION_FAILED`: the input is unsuitable or invalid. This is a\n  permanent failure; retrying the same input will not succeed. It\n  covers a failure that you can fix with a change of the input. For\n  example, a 3D asset edit of a model that the edit does not support\n  yet, such as a model with 2 or more colour textures, fails with this\n  code and a message that names the cause. An upload status read uses\n  the same word for a file that the API could not check. That case is\n  retryable (see `ErrorCode`).\n- `PIPELINE_ERROR`: PBR generation started but failed.\n  Generally retryable.\n- `TIMEOUT`: the item exceeded its time budget. Generally retryable.\n- `INTERNAL`: an unexpected server error. Generally retryable.\n"},"message":{"type":"string","description":"Human-readable explanation of the failure. The message can include\nnames from the file that you uploaded, such as a material name.\n"}}},"Error":{"type":"object","required":["error"],"description":"Canonical error envelope. The error payload is nested under the `error`\nkey.\n","properties":{"error":{"$ref":"#/components/schemas/ErrorBody"}}},"ErrorBody":{"type":"object","description":"The body of an error response. The `code` is a stable value clients can\nbranch on; the `message` is a human-readable explanation.\n","required":["code","message"],"properties":{"code":{"$ref":"#/components/schemas/ErrorCode"},"message":{"type":"string","description":"Human-readable description of the error.\n"},"details":{"type":["object","null"],"description":"Additional structured information about the error, or `null` if no\nextra details are available.\n","additionalProperties":true}}},"ErrorCode":{"type":"string","description":"Machine-readable error code. The enum contains every code that this API\ncan return.\n\nPossible enum values:\n- `INVALID_REQUEST`: the request body or a query parameter failed validation.\n- `MALFORMED_UUID`: a path parameter that should be a UUID is not one.\n- `UNAUTHORIZED`: the request could not be authenticated.\n- `API_KEY_MISSING`: no API key was supplied.\n- `API_KEY_INVALID`: the supplied API key does not match any known key.\n- `API_KEY_REVOKED`: the supplied API key has been revoked.\n- `PLAN_FEATURE_REQUIRED`: the account plan does not include a required feature.\n- `SUBSCRIPTION_FEATURE_REQUIRED`: a new Marketplace purchase requires a paid plan or assigned seat.\n- `PRICE_CHANGED`: the selected price differs from the price in the request.\n- `VERSION_CHANGED`: the selected Marketplace version differs from the requested version.\n- `IDEMPOTENCY_CONFLICT`: the same key was used for a different purchase request.\n- `DOWNLOAD_UNAVAILABLE`: the retained private content is temporarily unavailable.\n- `CATALOGUE_CHANGED`: the catalogue changed during pagination; restart the query.\n- `LISTING_NOT_FOUND`: the selected listing is absent or delisted.\n- `MARKETPLACE_DOWNLOAD_ONLY`: Marketplace content supports download and cannot enter generation.\n- `INSUFFICIENT_CREDITS`: the account does not have enough credits for this request.\n- `QUOTA_EXCEEDED`: a usage quota for the account has been exceeded.\n- `FILE_TOO_LARGE`: an uploaded or referenced file exceeds the allowed size.\n- `UPLOAD_TIMEOUT`: an image search upload exceeded 60 seconds. Try a smaller file or a faster connection.\n- `INVALID_CONTENT_TYPE`: a file's content type does not match its declared or expected type.\n- `UNSUPPORTED_ASSET_FORMAT`: a file's extension is not one of the supported asset formats.\n- `FORMAT_NOT_AVAILABLE`: the requested downloadable format does not exist for this asset.\n- `INVALID_FBX_ZIP_STRUCTURE`: a `.zip` upload is not a valid FBX bundle. The error message says why.\n- `IMAGE_RESOLUTION_EXCEEDED`: an image is larger than 4096 by 4096 pixels, or a texture in a mesh file is larger than 8192 by 8192 pixels.\n- `VALIDATION_FAILED`: the API could not check the uploaded file. Upload it again. This is the `error.code` of an upload status read. A failed batch item or run uses the same word with another meaning (see `ExecutionErrorBody`).\n- `UPLOAD_EXPIRED`: the upload did not finish within 1 hour. Upload the file again.\n- `MESH_REQUIREMENTS_NOT_MET`: the mesh file has no polygon geometry, no base-color texture, no UV coordinates, or it names a texture that the upload does not hold.\n- `COLLECTION_NOT_FOUND`: the collection does not exist in your account.\n- `PROJECT_NOT_FOUND`: the project does not exist, or another account owns it.\n- `GRAPH_NOT_FOUND`: the graph does not exist in this project.\n- `NODE_NOT_FOUND`: the node does not exist in this graph.\n- `GROUP_NOT_FOUND`: the group does not exist in this graph.\n- `PROJECT_NODE_LIMIT_EXCEEDED`: the project already holds as many nodes as it may hold.\n- `CONFLICT`: the request conflicts with the current state, so the server made no change.\n- `ASSET_NOT_FOUND`: the asset does not exist or does not belong to the account.\n- `ASSET_IN_USE`: an active execution uses a component, or another PBR execution shares a source or target component.\n- `BATCH_TOO_LARGE`: the batch contains more items than the endpoint allows.\n- `CONCURRENT_EXECUTION_LIMIT_EXCEEDED`: the account has reached its limit on simultaneous submissions.\n- `PBR_FAILURE_CAP_EXCEEDED`: the account has too many recent failed PBR runs; new submissions are temporarily blocked.\n- `EXECUTION_NOT_FOUND`: the execution or batch does not exist or does not belong to the account.\n- `EXECUTION_IN_PROGRESS`: the request conflicts with an execution that is still running.\n- `RATE_LIMITED`: the API key's rate limit was exceeded.\n- `SERVICE_UNAVAILABLE`: the requested operation is temporarily unavailable; retry later.\n- `MAINTENANCE_MODE`: the API is temporarily unavailable for maintenance.\n- `INTERNAL_SERVER_ERROR`: an unexpected server error occurred.\n- `WORKFLOW_CREATION_FAILED`: the API could not start the requested generation.\n- `WORKFLOW_API_ERROR`: generation stopped because of an unexpected error.\n","enum":["INVALID_REQUEST","MALFORMED_UUID","UNAUTHORIZED","API_KEY_MISSING","API_KEY_INVALID","API_KEY_REVOKED","PLAN_FEATURE_REQUIRED","SUBSCRIPTION_FEATURE_REQUIRED","PRICE_CHANGED","VERSION_CHANGED","IDEMPOTENCY_CONFLICT","DOWNLOAD_UNAVAILABLE","CATALOGUE_CHANGED","LISTING_NOT_FOUND","MARKETPLACE_DOWNLOAD_ONLY","INSUFFICIENT_CREDITS","QUOTA_EXCEEDED","FILE_TOO_LARGE","UPLOAD_TIMEOUT","INVALID_CONTENT_TYPE","UNSUPPORTED_ASSET_FORMAT","FORMAT_NOT_AVAILABLE","INVALID_FBX_ZIP_STRUCTURE","IMAGE_RESOLUTION_EXCEEDED","VALIDATION_FAILED","UPLOAD_EXPIRED","MESH_REQUIREMENTS_NOT_MET","COLLECTION_NOT_FOUND","PROJECT_NOT_FOUND","GRAPH_NOT_FOUND","NODE_NOT_FOUND","GROUP_NOT_FOUND","PROJECT_NODE_LIMIT_EXCEEDED","CONFLICT","ASSET_NOT_FOUND","ASSET_IN_USE","BATCH_TOO_LARGE","CONCURRENT_EXECUTION_LIMIT_EXCEEDED","PBR_FAILURE_CAP_EXCEEDED","EXECUTION_NOT_FOUND","EXECUTION_IN_PROGRESS","RATE_LIMITED","SERVICE_UNAVAILABLE","MAINTENANCE_MODE","INTERNAL_SERVER_ERROR","WORKFLOW_CREATION_FAILED","WORKFLOW_API_ERROR"]},"PlanFeatureRequiredError":{"type":"object","required":["error"],"description":"The error envelope for a missing subscription feature.","properties":{"error":{"type":"object","required":["code","message","details"],"description":"The missing-feature error body.","properties":{"code":{"type":"string","const":"PLAN_FEATURE_REQUIRED","description":"Stable error code for a missing subscription feature."},"message":{"type":"string","description":"Human-readable description of the error."},"details":{"$ref":"#/components/schemas/PlanFeatureRequiredErrorDetails"}}}}},"PlanFeatureRequiredErrorDetails":{"type":"object","required":["feature","upgrade_plan_codes"],"description":"The missing feature and the active plans that provide it.","properties":{"feature":{"$ref":"#/components/schemas/SubscriptionFeature"},"upgrade_plan_codes":{"type":"array","description":"Active plans available for purchase that provide the missing\nfeature. The array uses plan class and opaque code order.\n","items":{"$ref":"#/components/schemas/SubscriptionPlanCode"}}}},"SubscriptionFeature":{"type":"string","description":"Product capability available to the account.\n\nPossible enum values:\n- `commercial_license`: commercial-use rights.\n- `api_pbr`: PBR generation through the Public Platform API.\n- `api_asset_download`: asset downloads through the Public Platform API.\n- `custom_polycount`: custom polygon counts for 3D generation.\n- `image_edit`: image editing.\n- `multiview`: multiview generation.\n- `asset_download`: asset downloads in the Marso web app.\n- `api_access`: access to the Public Platform API.\n- `scene_generation`: scene generation and multi-asset runs in the Marso web app.\n- `application_apps`: the Sim Ready and Game Ready apps.\n","enum":["commercial_license","api_pbr","api_asset_download","custom_polycount","image_edit","multiview","asset_download","api_access","scene_generation","application_apps"]},"SubscriptionPlanCode":{"type":"string","pattern":"^plan_[A-Za-z0-9]{7,64}$","description":"Opaque and immutable code for a subscription plan. A plan display name\ncan change. The code starts with `plan_`, followed by 7 to 64 ASCII\nletters or digits.\n"}},"responses":{"Unauthorized":{"description":"The API key is missing, invalid, or revoked. Possible error codes:\n`API_KEY_MISSING`, `API_KEY_INVALID`, `API_KEY_REVOKED`, or `UNAUTHORIZED`.\n","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"PlanFeatureRequired":{"description":"The account does not have the plan feature required by this operation\n(`PLAN_FEATURE_REQUIRED`). The Free plan lacks `api_access`, so every\noperation refuses a Free account with this response. An account that\nlacks `api_pbr` or `api_asset_download` also gets it on the routes that\nneed those features.\n","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlanFeatureRequiredError"}}}},"RateLimitedOrCapped":{"description":"The request could not be accepted because of a rate or concurrency limit.\nPossible error codes:\n\n- `RATE_LIMITED`: the API-key rate limit was exceeded.\n- `CONCURRENT_EXECUTION_LIMIT_EXCEEDED`: a single submission exceeded\n  the concurrency limit.\n- `PBR_FAILURE_CAP_EXCEEDED`: too many consecutive failures; check the\n  input before retrying.\n","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"paths":{"/batch/pbr/image":{"post":{"operationId":"createImagePBRBatch","tags":["pbr"],"summary":"Generate PBR materials from images.","description":"Submits one or more ready image assets for image to PBR generation. The\nimage counterpart of `POST /batch/pbr/mesh`. It takes `detail_transfer`\nand the image-only `tileable` option, but not `output_2d`. Image results\ndo not include a USDZ file.\n\nThe request returns immediately with a `batch_id` (`202`). Poll\n`GET /batch/{batch_id}` for the batch status and each image's generated\nmaterial maps and download URLs. Credits are reserved when you submit\nand refunded automatically for any item that fails.\n","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreatePBRImageBatchRequest"}}}},"responses":{"202":{"description":"Batch created and queued.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BatchResponse"}}}},"400":{"description":"The request is invalid because the `asset_ids` list is empty or too\nlong, an asset was not found or is not owned by the authenticated account, an asset\nis not an image, or an asset is not `ready` (`INVALID_REQUEST`).\n","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/PlanFeatureRequired"},"429":{"$ref":"#/components/responses/RateLimitedOrCapped"}}}}}}
```

## List your batches.

> Returns your PBR batches, newest first, as paginated summaries. Each\
> summary contains the batch ID, status, per-status counts, and timestamps.\
> Per-item results are not included; use \`GET /batch/{batch\_id}\` to fetch\
> the full results for a batch.<br>

```json
{"openapi":"3.1.0","info":{"title":"Marso Studio API","version":"0.0.1"},"tags":[{"name":"pbr","description":"Batches group one or more PBR generation jobs together. Use these endpoints\nto submit 3D mesh to PBR or image to PBR batches, poll a batch for progress,\nand list recent batches.\n"}],"servers":[{"url":"https://api.marso.app/api/public/v1/","description":"Production server."}],"security":[{"apiKey":[]}],"components":{"securitySchemes":{"apiKey":{"type":"http","scheme":"bearer","description":"API key created in the Marso Studio dashboard. The value has the form\n`mxr_live_<entropy>` and is sent as a Bearer token in the `Authorization`\nheader.\n"}},"parameters":{"Limit":{"name":"limit","in":"query","required":false,"description":"Page size. Defaults to 50 and is clamped to a maximum of 200. Non-positive\nor non-numeric values fall back to the default.\n","schema":{"type":"integer","minimum":1,"default":50}},"Cursor":{"name":"cursor","in":"query","required":false,"description":"Opaque pagination cursor returned by the previous page as `next_cursor`.\nOmit on the first request.\n","schema":{"type":"string"}}},"schemas":{"BatchListResponse":{"type":"object","required":["batches"],"description":"Response from `GET /batch`. A paginated list of batch summaries.\n","properties":{"batches":{"type":"array","description":"Batch summaries, newest first.\n","items":{"$ref":"#/components/schemas/BatchSummaryBody"}},"next_cursor":{"type":"string","description":"Opaque cursor for the next page. Absent or empty when the result set\nis exhausted.\n"}}},"BatchSummaryBody":{"type":"object","required":["batch_id","status","request_counts","created_at"],"description":"One summary in `GET /batch`. Contains the batch status without per-item\nresults.\n","properties":{"batch_id":{"type":"string","description":"Unique identifier of the batch.\n"},"status":{"$ref":"#/components/schemas/BatchStatus"},"request_counts":{"$ref":"#/components/schemas/BatchCountsBody"},"created_at":{"$ref":"#/components/schemas/EpochMillis"},"completed_at":{"$ref":"#/components/schemas/NullableEpochMillis"},"failed_at":{"$ref":"#/components/schemas/NullableEpochMillis"}}},"BatchStatus":{"type":"string","description":"Aggregate status of a PBR batch.\n\nPossible enum values:\n- `QUEUED`: the batch is waiting to start.\n- `RUNNING`: at least one item is being processed.\n- `COMPLETED`: every item succeeded.\n- `PARTIALLY_COMPLETED`: the batch finished with a mix of successes and\n  failures.\n- `FAILED`: every item failed.\n","enum":["QUEUED","RUNNING","COMPLETED","PARTIALLY_COMPLETED","FAILED"]},"BatchCountsBody":{"type":"object","required":["total","completed","failed","pending"],"description":"Count of items in a batch, grouped by status.\n","properties":{"total":{"type":"integer","description":"Total number of items in the batch.\n"},"completed":{"type":"integer","description":"Number of items that have reached a terminal state, whether they\nsucceeded or failed.\n"},"failed":{"type":"integer","description":"Number of items that failed.\n"},"pending":{"type":"integer","description":"Number of items that are still queued or running.\n"}}},"EpochMillis":{"type":"integer","format":"int64","description":"Time represented as milliseconds since the Unix epoch.\n"},"NullableEpochMillis":{"type":["integer","null"],"format":"int64","description":"Time represented as milliseconds since the Unix epoch, or `null` when the\nfield has not been set.\n"},"Error":{"type":"object","required":["error"],"description":"Canonical error envelope. The error payload is nested under the `error`\nkey.\n","properties":{"error":{"$ref":"#/components/schemas/ErrorBody"}}},"ErrorBody":{"type":"object","description":"The body of an error response. The `code` is a stable value clients can\nbranch on; the `message` is a human-readable explanation.\n","required":["code","message"],"properties":{"code":{"$ref":"#/components/schemas/ErrorCode"},"message":{"type":"string","description":"Human-readable description of the error.\n"},"details":{"type":["object","null"],"description":"Additional structured information about the error, or `null` if no\nextra details are available.\n","additionalProperties":true}}},"ErrorCode":{"type":"string","description":"Machine-readable error code. The enum contains every code that this API\ncan return.\n\nPossible enum values:\n- `INVALID_REQUEST`: the request body or a query parameter failed validation.\n- `MALFORMED_UUID`: a path parameter that should be a UUID is not one.\n- `UNAUTHORIZED`: the request could not be authenticated.\n- `API_KEY_MISSING`: no API key was supplied.\n- `API_KEY_INVALID`: the supplied API key does not match any known key.\n- `API_KEY_REVOKED`: the supplied API key has been revoked.\n- `PLAN_FEATURE_REQUIRED`: the account plan does not include a required feature.\n- `SUBSCRIPTION_FEATURE_REQUIRED`: a new Marketplace purchase requires a paid plan or assigned seat.\n- `PRICE_CHANGED`: the selected price differs from the price in the request.\n- `VERSION_CHANGED`: the selected Marketplace version differs from the requested version.\n- `IDEMPOTENCY_CONFLICT`: the same key was used for a different purchase request.\n- `DOWNLOAD_UNAVAILABLE`: the retained private content is temporarily unavailable.\n- `CATALOGUE_CHANGED`: the catalogue changed during pagination; restart the query.\n- `LISTING_NOT_FOUND`: the selected listing is absent or delisted.\n- `MARKETPLACE_DOWNLOAD_ONLY`: Marketplace content supports download and cannot enter generation.\n- `INSUFFICIENT_CREDITS`: the account does not have enough credits for this request.\n- `QUOTA_EXCEEDED`: a usage quota for the account has been exceeded.\n- `FILE_TOO_LARGE`: an uploaded or referenced file exceeds the allowed size.\n- `UPLOAD_TIMEOUT`: an image search upload exceeded 60 seconds. Try a smaller file or a faster connection.\n- `INVALID_CONTENT_TYPE`: a file's content type does not match its declared or expected type.\n- `UNSUPPORTED_ASSET_FORMAT`: a file's extension is not one of the supported asset formats.\n- `FORMAT_NOT_AVAILABLE`: the requested downloadable format does not exist for this asset.\n- `INVALID_FBX_ZIP_STRUCTURE`: a `.zip` upload is not a valid FBX bundle. The error message says why.\n- `IMAGE_RESOLUTION_EXCEEDED`: an image is larger than 4096 by 4096 pixels, or a texture in a mesh file is larger than 8192 by 8192 pixels.\n- `VALIDATION_FAILED`: the API could not check the uploaded file. Upload it again. This is the `error.code` of an upload status read. A failed batch item or run uses the same word with another meaning (see `ExecutionErrorBody`).\n- `UPLOAD_EXPIRED`: the upload did not finish within 1 hour. Upload the file again.\n- `MESH_REQUIREMENTS_NOT_MET`: the mesh file has no polygon geometry, no base-color texture, no UV coordinates, or it names a texture that the upload does not hold.\n- `COLLECTION_NOT_FOUND`: the collection does not exist in your account.\n- `PROJECT_NOT_FOUND`: the project does not exist, or another account owns it.\n- `GRAPH_NOT_FOUND`: the graph does not exist in this project.\n- `NODE_NOT_FOUND`: the node does not exist in this graph.\n- `GROUP_NOT_FOUND`: the group does not exist in this graph.\n- `PROJECT_NODE_LIMIT_EXCEEDED`: the project already holds as many nodes as it may hold.\n- `CONFLICT`: the request conflicts with the current state, so the server made no change.\n- `ASSET_NOT_FOUND`: the asset does not exist or does not belong to the account.\n- `ASSET_IN_USE`: an active execution uses a component, or another PBR execution shares a source or target component.\n- `BATCH_TOO_LARGE`: the batch contains more items than the endpoint allows.\n- `CONCURRENT_EXECUTION_LIMIT_EXCEEDED`: the account has reached its limit on simultaneous submissions.\n- `PBR_FAILURE_CAP_EXCEEDED`: the account has too many recent failed PBR runs; new submissions are temporarily blocked.\n- `EXECUTION_NOT_FOUND`: the execution or batch does not exist or does not belong to the account.\n- `EXECUTION_IN_PROGRESS`: the request conflicts with an execution that is still running.\n- `RATE_LIMITED`: the API key's rate limit was exceeded.\n- `SERVICE_UNAVAILABLE`: the requested operation is temporarily unavailable; retry later.\n- `MAINTENANCE_MODE`: the API is temporarily unavailable for maintenance.\n- `INTERNAL_SERVER_ERROR`: an unexpected server error occurred.\n- `WORKFLOW_CREATION_FAILED`: the API could not start the requested generation.\n- `WORKFLOW_API_ERROR`: generation stopped because of an unexpected error.\n","enum":["INVALID_REQUEST","MALFORMED_UUID","UNAUTHORIZED","API_KEY_MISSING","API_KEY_INVALID","API_KEY_REVOKED","PLAN_FEATURE_REQUIRED","SUBSCRIPTION_FEATURE_REQUIRED","PRICE_CHANGED","VERSION_CHANGED","IDEMPOTENCY_CONFLICT","DOWNLOAD_UNAVAILABLE","CATALOGUE_CHANGED","LISTING_NOT_FOUND","MARKETPLACE_DOWNLOAD_ONLY","INSUFFICIENT_CREDITS","QUOTA_EXCEEDED","FILE_TOO_LARGE","UPLOAD_TIMEOUT","INVALID_CONTENT_TYPE","UNSUPPORTED_ASSET_FORMAT","FORMAT_NOT_AVAILABLE","INVALID_FBX_ZIP_STRUCTURE","IMAGE_RESOLUTION_EXCEEDED","VALIDATION_FAILED","UPLOAD_EXPIRED","MESH_REQUIREMENTS_NOT_MET","COLLECTION_NOT_FOUND","PROJECT_NOT_FOUND","GRAPH_NOT_FOUND","NODE_NOT_FOUND","GROUP_NOT_FOUND","PROJECT_NODE_LIMIT_EXCEEDED","CONFLICT","ASSET_NOT_FOUND","ASSET_IN_USE","BATCH_TOO_LARGE","CONCURRENT_EXECUTION_LIMIT_EXCEEDED","PBR_FAILURE_CAP_EXCEEDED","EXECUTION_NOT_FOUND","EXECUTION_IN_PROGRESS","RATE_LIMITED","SERVICE_UNAVAILABLE","MAINTENANCE_MODE","INTERNAL_SERVER_ERROR","WORKFLOW_CREATION_FAILED","WORKFLOW_API_ERROR"]},"PlanFeatureRequiredError":{"type":"object","required":["error"],"description":"The error envelope for a missing subscription feature.","properties":{"error":{"type":"object","required":["code","message","details"],"description":"The missing-feature error body.","properties":{"code":{"type":"string","const":"PLAN_FEATURE_REQUIRED","description":"Stable error code for a missing subscription feature."},"message":{"type":"string","description":"Human-readable description of the error."},"details":{"$ref":"#/components/schemas/PlanFeatureRequiredErrorDetails"}}}}},"PlanFeatureRequiredErrorDetails":{"type":"object","required":["feature","upgrade_plan_codes"],"description":"The missing feature and the active plans that provide it.","properties":{"feature":{"$ref":"#/components/schemas/SubscriptionFeature"},"upgrade_plan_codes":{"type":"array","description":"Active plans available for purchase that provide the missing\nfeature. The array uses plan class and opaque code order.\n","items":{"$ref":"#/components/schemas/SubscriptionPlanCode"}}}},"SubscriptionFeature":{"type":"string","description":"Product capability available to the account.\n\nPossible enum values:\n- `commercial_license`: commercial-use rights.\n- `api_pbr`: PBR generation through the Public Platform API.\n- `api_asset_download`: asset downloads through the Public Platform API.\n- `custom_polycount`: custom polygon counts for 3D generation.\n- `image_edit`: image editing.\n- `multiview`: multiview generation.\n- `asset_download`: asset downloads in the Marso web app.\n- `api_access`: access to the Public Platform API.\n- `scene_generation`: scene generation and multi-asset runs in the Marso web app.\n- `application_apps`: the Sim Ready and Game Ready apps.\n","enum":["commercial_license","api_pbr","api_asset_download","custom_polycount","image_edit","multiview","asset_download","api_access","scene_generation","application_apps"]},"SubscriptionPlanCode":{"type":"string","pattern":"^plan_[A-Za-z0-9]{7,64}$","description":"Opaque and immutable code for a subscription plan. A plan display name\ncan change. The code starts with `plan_`, followed by 7 to 64 ASCII\nletters or digits.\n"}},"responses":{"Unauthorized":{"description":"The API key is missing, invalid, or revoked. Possible error codes:\n`API_KEY_MISSING`, `API_KEY_INVALID`, `API_KEY_REVOKED`, or `UNAUTHORIZED`.\n","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"PlanFeatureRequired":{"description":"The account does not have the plan feature required by this operation\n(`PLAN_FEATURE_REQUIRED`). The Free plan lacks `api_access`, so every\noperation refuses a Free account with this response. An account that\nlacks `api_pbr` or `api_asset_download` also gets it on the routes that\nneed those features.\n","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlanFeatureRequiredError"}}}},"RateLimited":{"description":"Rate limit exceeded (`RATE_LIMITED`). The response includes a `Retry-After`\nheader with the number of seconds to wait before retrying, plus the\n`X-RateLimit-*` headers. Those headers are also present on successful (2xx)\nresponses so a client can pace itself.\n","headers":{"Retry-After":{"description":"Seconds to wait before retrying.","schema":{"type":"integer"}},"X-RateLimit-Limit":{"description":"Request burst capacity for the API key.","schema":{"type":"integer"}},"X-RateLimit-Remaining":{"description":"Requests remaining in the current burst window.","schema":{"type":"integer"}},"X-RateLimit-Reset":{"description":"Unix epoch seconds when the burst capacity is fully restored.\n","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"paths":{"/batch":{"get":{"operationId":"listBatches","tags":["pbr"],"summary":"List your batches.","description":"Returns your PBR batches, newest first, as paginated summaries. Each\nsummary contains the batch ID, status, per-status counts, and timestamps.\nPer-item results are not included; use `GET /batch/{batch_id}` to fetch\nthe full results for a batch.\n","parameters":[{"$ref":"#/components/parameters/Limit"},{"$ref":"#/components/parameters/Cursor"}],"responses":{"200":{"description":"A page of batch summaries.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BatchListResponse"}}}},"400":{"description":"A query parameter is invalid (`INVALID_REQUEST`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/PlanFeatureRequired"},"429":{"$ref":"#/components/responses/RateLimited"}}}}}}
```

## Retrieve a batch.

> Returns a batch's status, per-item counts, and the finished results for\
> each item. As items finish, the response includes their generated material\
> maps with ready-to-use download URLs, or a typed error for failed items.\
> This is the endpoint you poll after submitting a PBR batch. \`items\` is\
> empty until at least one item has reached a terminal state.<br>

```json
{"openapi":"3.1.0","info":{"title":"Marso Studio API","version":"0.0.1"},"tags":[{"name":"pbr","description":"Batches group one or more PBR generation jobs together. Use these endpoints\nto submit 3D mesh to PBR or image to PBR batches, poll a batch for progress,\nand list recent batches.\n"}],"servers":[{"url":"https://api.marso.app/api/public/v1/","description":"Production server."}],"security":[{"apiKey":[]}],"components":{"securitySchemes":{"apiKey":{"type":"http","scheme":"bearer","description":"API key created in the Marso Studio dashboard. The value has the form\n`mxr_live_<entropy>` and is sent as a Bearer token in the `Authorization`\nheader.\n"}},"parameters":{"BatchId":{"name":"batch_id","in":"path","required":true,"description":"Unique identifier of the batch.","schema":{"type":"string","format":"uuid"}}},"schemas":{"BatchResponse":{"type":"object","required":["batch_id","status","request_counts","created_at","items"],"description":"Body returned by `POST /batch/pbr/*` (202) and `GET /batch/{batch_id}`\n(200). On submission `items` is empty; poll the batch to populate it.\n`completed_at` is set once the batch reaches a terminal status; `failed_at`\nis set only when that terminal status is `FAILED`.\n","properties":{"batch_id":{"type":"string","description":"Unique identifier of the batch.\n"},"status":{"$ref":"#/components/schemas/BatchStatus"},"request_counts":{"$ref":"#/components/schemas/BatchCountsBody"},"created_at":{"$ref":"#/components/schemas/EpochMillis"},"completed_at":{"$ref":"#/components/schemas/NullableEpochMillis"},"failed_at":{"$ref":"#/components/schemas/NullableEpochMillis"},"items":{"type":"array","description":"Finished results for each item. Empty until at least one item has\nfinished.\n","items":{"$ref":"#/components/schemas/BatchItemBody"}}}},"BatchStatus":{"type":"string","description":"Aggregate status of a PBR batch.\n\nPossible enum values:\n- `QUEUED`: the batch is waiting to start.\n- `RUNNING`: at least one item is being processed.\n- `COMPLETED`: every item succeeded.\n- `PARTIALLY_COMPLETED`: the batch finished with a mix of successes and\n  failures.\n- `FAILED`: every item failed.\n","enum":["QUEUED","RUNNING","COMPLETED","PARTIALLY_COMPLETED","FAILED"]},"BatchCountsBody":{"type":"object","required":["total","completed","failed","pending"],"description":"Count of items in a batch, grouped by status.\n","properties":{"total":{"type":"integer","description":"Total number of items in the batch.\n"},"completed":{"type":"integer","description":"Number of items that have reached a terminal state, whether they\nsucceeded or failed.\n"},"failed":{"type":"integer","description":"Number of items that failed.\n"},"pending":{"type":"integer","description":"Number of items that are still queued or running.\n"}}},"EpochMillis":{"type":"integer","format":"int64","description":"Time represented as milliseconds since the Unix epoch.\n"},"NullableEpochMillis":{"type":["integer","null"],"format":"int64","description":"Time represented as milliseconds since the Unix epoch, or `null` when the\nfield has not been set.\n"},"BatchItemBody":{"type":"object","required":["execution_id","status","created_at"],"description":"One item's result within a batch. `asset_id` is the source asset you\nsubmitted, so a failed item is always traceable to its input.\n`pbr_material_assets` maps each `\"<slot>/<pass>\"` to its generated texture\nasset and a ready-to-use `download_url`, so you can pull every result\ndirectly from the poll response. `usdz` is the textured 3D model of a\nsucceeded mesh item, in the same shape. `thumbnail_url` is a preview\nimage. It is always present on a succeeded mesh item, and best-effort on\na succeeded image item: such an item may finish without one. A failed\nitem carries `error`, and no other item does.\n","properties":{"execution_id":{"type":"string","description":"Unique identifier of the execution for this item.\n"},"asset_id":{"type":"string","description":"ID of the source asset that was submitted for this item.\n"},"status":{"$ref":"#/components/schemas/ExecutionStatus"},"thumbnail_url":{"type":"string","format":"uri","description":"URL of a preview image. Always present on a succeeded mesh item;\nbest-effort on a succeeded image item.\n"},"thumbnail_url_expires_at":{"$ref":"#/components/schemas/EpochMillis"},"pbr_material_assets":{"type":"object","description":"Map from `\"<slot>/<pass>\"` to the generated texture asset. Only\npresent on succeeded items. `\"<slot>/normal\"` is an example of a key.\nThe map can hold other keys, and a key can be absent. Check that a key\nexists before you read it. When a 3D mesh to PBR job returns a normal\nmap, the map uses OpenGL tangent space. Do not assume a space for the\nnormal map of an image to PBR job.\n","additionalProperties":{"$ref":"#/components/schemas/BatchMaterialAssetBody"}},"usdz":{"allOf":[{"$ref":"#/components/schemas/BatchMaterialAssetBody"}],"description":"The `material.usdz` of the item: one self-contained file that carries\nthe 3D model and its generated textures. A succeeded mesh item\ncarries it, and an image item carries none. An item that finished\nsome time ago may carry none.\n"},"error":{"$ref":"#/components/schemas/ExecutionErrorBody"},"created_at":{"$ref":"#/components/schemas/EpochMillis"}}},"ExecutionStatus":{"type":"string","description":"Status of a single item within a batch.\n\nPossible enum values:\n- `QUEUED`: the item is waiting to start.\n- `RUNNING`: the item is being processed.\n- `SUCCEEDED`: the item completed successfully.\n- `PARTIAL_SUCCEEDED`: the item completed but some optional outputs are\n  missing.\n- `FAILED`: the item failed. See the `error` field for details.\n- `CANCELED`: the item stopped without producing a result, and any\n  credits held for it are returned. Submit the request again to retry\n  it.\n","enum":["QUEUED","RUNNING","SUCCEEDED","PARTIAL_SUCCEEDED","FAILED","CANCELED"]},"BatchMaterialAssetBody":{"type":"object","required":["asset_id"],"description":"One generated PBR result asset within a batch item. `download_url` is a\nready-to-use link that expires at `expires_at`.\n","properties":{"asset_id":{"type":"string","description":"ID of the generated asset.\n"},"download_url":{"type":"string","format":"uri","description":"Presigned URL for downloading the generated asset. Valid until\n`expires_at`.\n"},"expires_at":{"$ref":"#/components/schemas/EpochMillis"}}},"ExecutionErrorBody":{"type":"object","description":"Failure detail for a failed batch item, and for a failed run that\n`GET /mcp/executions/{execution_id}` reads. The `code` is a stable,\nmachine-readable class drawn from a closed enum so clients can branch on\nthe failure kind. The `message` carries human-readable detail. Only\n`VALIDATION_FAILED` is permanent; do not retry the same input. The other\ncodes are generally retryable.\n","required":["code","message"],"properties":{"code":{"type":"string","enum":["VALIDATION_FAILED","PIPELINE_ERROR","TIMEOUT","INTERNAL"],"description":"Machine-readable class of the failure.\n\nPossible enum values:\n- `VALIDATION_FAILED`: the input is unsuitable or invalid. This is a\n  permanent failure; retrying the same input will not succeed. It\n  covers a failure that you can fix with a change of the input. For\n  example, a 3D asset edit of a model that the edit does not support\n  yet, such as a model with 2 or more colour textures, fails with this\n  code and a message that names the cause. An upload status read uses\n  the same word for a file that the API could not check. That case is\n  retryable (see `ErrorCode`).\n- `PIPELINE_ERROR`: PBR generation started but failed.\n  Generally retryable.\n- `TIMEOUT`: the item exceeded its time budget. Generally retryable.\n- `INTERNAL`: an unexpected server error. Generally retryable.\n"},"message":{"type":"string","description":"Human-readable explanation of the failure. The message can include\nnames from the file that you uploaded, such as a material name.\n"}}},"Error":{"type":"object","required":["error"],"description":"Canonical error envelope. The error payload is nested under the `error`\nkey.\n","properties":{"error":{"$ref":"#/components/schemas/ErrorBody"}}},"ErrorBody":{"type":"object","description":"The body of an error response. The `code` is a stable value clients can\nbranch on; the `message` is a human-readable explanation.\n","required":["code","message"],"properties":{"code":{"$ref":"#/components/schemas/ErrorCode"},"message":{"type":"string","description":"Human-readable description of the error.\n"},"details":{"type":["object","null"],"description":"Additional structured information about the error, or `null` if no\nextra details are available.\n","additionalProperties":true}}},"ErrorCode":{"type":"string","description":"Machine-readable error code. The enum contains every code that this API\ncan return.\n\nPossible enum values:\n- `INVALID_REQUEST`: the request body or a query parameter failed validation.\n- `MALFORMED_UUID`: a path parameter that should be a UUID is not one.\n- `UNAUTHORIZED`: the request could not be authenticated.\n- `API_KEY_MISSING`: no API key was supplied.\n- `API_KEY_INVALID`: the supplied API key does not match any known key.\n- `API_KEY_REVOKED`: the supplied API key has been revoked.\n- `PLAN_FEATURE_REQUIRED`: the account plan does not include a required feature.\n- `SUBSCRIPTION_FEATURE_REQUIRED`: a new Marketplace purchase requires a paid plan or assigned seat.\n- `PRICE_CHANGED`: the selected price differs from the price in the request.\n- `VERSION_CHANGED`: the selected Marketplace version differs from the requested version.\n- `IDEMPOTENCY_CONFLICT`: the same key was used for a different purchase request.\n- `DOWNLOAD_UNAVAILABLE`: the retained private content is temporarily unavailable.\n- `CATALOGUE_CHANGED`: the catalogue changed during pagination; restart the query.\n- `LISTING_NOT_FOUND`: the selected listing is absent or delisted.\n- `MARKETPLACE_DOWNLOAD_ONLY`: Marketplace content supports download and cannot enter generation.\n- `INSUFFICIENT_CREDITS`: the account does not have enough credits for this request.\n- `QUOTA_EXCEEDED`: a usage quota for the account has been exceeded.\n- `FILE_TOO_LARGE`: an uploaded or referenced file exceeds the allowed size.\n- `UPLOAD_TIMEOUT`: an image search upload exceeded 60 seconds. Try a smaller file or a faster connection.\n- `INVALID_CONTENT_TYPE`: a file's content type does not match its declared or expected type.\n- `UNSUPPORTED_ASSET_FORMAT`: a file's extension is not one of the supported asset formats.\n- `FORMAT_NOT_AVAILABLE`: the requested downloadable format does not exist for this asset.\n- `INVALID_FBX_ZIP_STRUCTURE`: a `.zip` upload is not a valid FBX bundle. The error message says why.\n- `IMAGE_RESOLUTION_EXCEEDED`: an image is larger than 4096 by 4096 pixels, or a texture in a mesh file is larger than 8192 by 8192 pixels.\n- `VALIDATION_FAILED`: the API could not check the uploaded file. Upload it again. This is the `error.code` of an upload status read. A failed batch item or run uses the same word with another meaning (see `ExecutionErrorBody`).\n- `UPLOAD_EXPIRED`: the upload did not finish within 1 hour. Upload the file again.\n- `MESH_REQUIREMENTS_NOT_MET`: the mesh file has no polygon geometry, no base-color texture, no UV coordinates, or it names a texture that the upload does not hold.\n- `COLLECTION_NOT_FOUND`: the collection does not exist in your account.\n- `PROJECT_NOT_FOUND`: the project does not exist, or another account owns it.\n- `GRAPH_NOT_FOUND`: the graph does not exist in this project.\n- `NODE_NOT_FOUND`: the node does not exist in this graph.\n- `GROUP_NOT_FOUND`: the group does not exist in this graph.\n- `PROJECT_NODE_LIMIT_EXCEEDED`: the project already holds as many nodes as it may hold.\n- `CONFLICT`: the request conflicts with the current state, so the server made no change.\n- `ASSET_NOT_FOUND`: the asset does not exist or does not belong to the account.\n- `ASSET_IN_USE`: an active execution uses a component, or another PBR execution shares a source or target component.\n- `BATCH_TOO_LARGE`: the batch contains more items than the endpoint allows.\n- `CONCURRENT_EXECUTION_LIMIT_EXCEEDED`: the account has reached its limit on simultaneous submissions.\n- `PBR_FAILURE_CAP_EXCEEDED`: the account has too many recent failed PBR runs; new submissions are temporarily blocked.\n- `EXECUTION_NOT_FOUND`: the execution or batch does not exist or does not belong to the account.\n- `EXECUTION_IN_PROGRESS`: the request conflicts with an execution that is still running.\n- `RATE_LIMITED`: the API key's rate limit was exceeded.\n- `SERVICE_UNAVAILABLE`: the requested operation is temporarily unavailable; retry later.\n- `MAINTENANCE_MODE`: the API is temporarily unavailable for maintenance.\n- `INTERNAL_SERVER_ERROR`: an unexpected server error occurred.\n- `WORKFLOW_CREATION_FAILED`: the API could not start the requested generation.\n- `WORKFLOW_API_ERROR`: generation stopped because of an unexpected error.\n","enum":["INVALID_REQUEST","MALFORMED_UUID","UNAUTHORIZED","API_KEY_MISSING","API_KEY_INVALID","API_KEY_REVOKED","PLAN_FEATURE_REQUIRED","SUBSCRIPTION_FEATURE_REQUIRED","PRICE_CHANGED","VERSION_CHANGED","IDEMPOTENCY_CONFLICT","DOWNLOAD_UNAVAILABLE","CATALOGUE_CHANGED","LISTING_NOT_FOUND","MARKETPLACE_DOWNLOAD_ONLY","INSUFFICIENT_CREDITS","QUOTA_EXCEEDED","FILE_TOO_LARGE","UPLOAD_TIMEOUT","INVALID_CONTENT_TYPE","UNSUPPORTED_ASSET_FORMAT","FORMAT_NOT_AVAILABLE","INVALID_FBX_ZIP_STRUCTURE","IMAGE_RESOLUTION_EXCEEDED","VALIDATION_FAILED","UPLOAD_EXPIRED","MESH_REQUIREMENTS_NOT_MET","COLLECTION_NOT_FOUND","PROJECT_NOT_FOUND","GRAPH_NOT_FOUND","NODE_NOT_FOUND","GROUP_NOT_FOUND","PROJECT_NODE_LIMIT_EXCEEDED","CONFLICT","ASSET_NOT_FOUND","ASSET_IN_USE","BATCH_TOO_LARGE","CONCURRENT_EXECUTION_LIMIT_EXCEEDED","PBR_FAILURE_CAP_EXCEEDED","EXECUTION_NOT_FOUND","EXECUTION_IN_PROGRESS","RATE_LIMITED","SERVICE_UNAVAILABLE","MAINTENANCE_MODE","INTERNAL_SERVER_ERROR","WORKFLOW_CREATION_FAILED","WORKFLOW_API_ERROR"]},"PlanFeatureRequiredError":{"type":"object","required":["error"],"description":"The error envelope for a missing subscription feature.","properties":{"error":{"type":"object","required":["code","message","details"],"description":"The missing-feature error body.","properties":{"code":{"type":"string","const":"PLAN_FEATURE_REQUIRED","description":"Stable error code for a missing subscription feature."},"message":{"type":"string","description":"Human-readable description of the error."},"details":{"$ref":"#/components/schemas/PlanFeatureRequiredErrorDetails"}}}}},"PlanFeatureRequiredErrorDetails":{"type":"object","required":["feature","upgrade_plan_codes"],"description":"The missing feature and the active plans that provide it.","properties":{"feature":{"$ref":"#/components/schemas/SubscriptionFeature"},"upgrade_plan_codes":{"type":"array","description":"Active plans available for purchase that provide the missing\nfeature. The array uses plan class and opaque code order.\n","items":{"$ref":"#/components/schemas/SubscriptionPlanCode"}}}},"SubscriptionFeature":{"type":"string","description":"Product capability available to the account.\n\nPossible enum values:\n- `commercial_license`: commercial-use rights.\n- `api_pbr`: PBR generation through the Public Platform API.\n- `api_asset_download`: asset downloads through the Public Platform API.\n- `custom_polycount`: custom polygon counts for 3D generation.\n- `image_edit`: image editing.\n- `multiview`: multiview generation.\n- `asset_download`: asset downloads in the Marso web app.\n- `api_access`: access to the Public Platform API.\n- `scene_generation`: scene generation and multi-asset runs in the Marso web app.\n- `application_apps`: the Sim Ready and Game Ready apps.\n","enum":["commercial_license","api_pbr","api_asset_download","custom_polycount","image_edit","multiview","asset_download","api_access","scene_generation","application_apps"]},"SubscriptionPlanCode":{"type":"string","pattern":"^plan_[A-Za-z0-9]{7,64}$","description":"Opaque and immutable code for a subscription plan. A plan display name\ncan change. The code starts with `plan_`, followed by 7 to 64 ASCII\nletters or digits.\n"}},"responses":{"Unauthorized":{"description":"The API key is missing, invalid, or revoked. Possible error codes:\n`API_KEY_MISSING`, `API_KEY_INVALID`, `API_KEY_REVOKED`, or `UNAUTHORIZED`.\n","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"PlanFeatureRequired":{"description":"The account does not have the plan feature required by this operation\n(`PLAN_FEATURE_REQUIRED`). The Free plan lacks `api_access`, so every\noperation refuses a Free account with this response. An account that\nlacks `api_pbr` or `api_asset_download` also gets it on the routes that\nneed those features.\n","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlanFeatureRequiredError"}}}},"BatchNotFound":{"description":"The batch does not exist or does not belong to your account (`EXECUTION_NOT_FOUND`).\n","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"RateLimited":{"description":"Rate limit exceeded (`RATE_LIMITED`). The response includes a `Retry-After`\nheader with the number of seconds to wait before retrying, plus the\n`X-RateLimit-*` headers. Those headers are also present on successful (2xx)\nresponses so a client can pace itself.\n","headers":{"Retry-After":{"description":"Seconds to wait before retrying.","schema":{"type":"integer"}},"X-RateLimit-Limit":{"description":"Request burst capacity for the API key.","schema":{"type":"integer"}},"X-RateLimit-Remaining":{"description":"Requests remaining in the current burst window.","schema":{"type":"integer"}},"X-RateLimit-Reset":{"description":"Unix epoch seconds when the burst capacity is fully restored.\n","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"paths":{"/batch/{batch_id}":{"get":{"operationId":"getBatch","tags":["pbr"],"summary":"Retrieve a batch.","description":"Returns a batch's status, per-item counts, and the finished results for\neach item. As items finish, the response includes their generated material\nmaps with ready-to-use download URLs, or a typed error for failed items.\nThis is the endpoint you poll after submitting a PBR batch. `items` is\nempty until at least one item has reached a terminal state.\n","parameters":[{"$ref":"#/components/parameters/BatchId"}],"responses":{"200":{"description":"Batch status and finished items.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BatchResponse"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/PlanFeatureRequired"},"404":{"$ref":"#/components/responses/BatchNotFound"},"429":{"$ref":"#/components/responses/RateLimited"}}}}}}
```

## Generate PBR materials from one 3D mesh.

> Submits one ready mesh asset for 3D mesh to PBR generation. The asset must\
> be a \`ready\` mesh uploaded with \`POST /uploads\` or \`POST /batch/uploads\`.\
> \
> The request returns immediately with an \`execution\_id\` (\`202\`). Poll\
> \`GET /pbr/executions/{execution\_id}\` for the status and, once the run\
> finishes, the generated material maps and their download URLs. Credits are\
> reserved when you submit and refunded automatically if the run fails, so\
> you are only charged for a successful result.\
> \
> Keep the mesh within the published limits that \`POST /uploads\` lists:\
> 1000 parts, 64 materials and 16 different colour textures. A texture is\
> 8192 pixels or smaller on its longest side. A mesh above a limit can fail.\
> \
> Use \`POST /batch/pbr/mesh\` instead when you have several meshes to submit\
> together.<br>

```json
{"openapi":"3.1.0","info":{"title":"Marso Studio API","version":"0.0.1"},"tags":[{"name":"pbr","description":"Batches group one or more PBR generation jobs together. Use these endpoints\nto submit 3D mesh to PBR or image to PBR batches, poll a batch for progress,\nand list recent batches.\n"}],"servers":[{"url":"https://api.marso.app/api/public/v1/","description":"Production server."}],"security":[{"apiKey":[]}],"components":{"securitySchemes":{"apiKey":{"type":"http","scheme":"bearer","description":"API key created in the Marso Studio dashboard. The value has the form\n`mxr_live_<entropy>` and is sent as a Bearer token in the `Authorization`\nheader.\n"}},"schemas":{"CreatePBRMeshExecutionRequest":{"type":"object","required":["asset_id"],"description":"Request body for `POST /pbr/execute/mesh`. Submit one ready mesh asset\nfor PBR generation.\n","properties":{"asset_id":{"type":"string","format":"uuid","description":"The ID of the mesh asset to process. Upload it with `POST /uploads`\nor `POST /batch/uploads` first. Its status must be `ready`.\n"},"options":{"$ref":"#/components/schemas/PublicPBRMeshOptions"}}},"PublicPBRMeshOptions":{"type":"object","additionalProperties":false,"description":"Tuning options for a mesh PBR run. Every field is optional, and an\nomitted field uses the system default.\n\nMesh options include `detail_transfer` and `output_2d`. They do not\ninclude `tileable`. Every mesh result includes a USDZ file. Unknown\nfields return a `400` response.\n","properties":{"detail_transfer":{"$ref":"#/components/schemas/PublicPBRDetailTransfer"},"output_2d":{"$ref":"#/components/schemas/PublicPBROutput2D"}}},"PublicPBRDetailTransfer":{"type":"object","additionalProperties":false,"description":"Settings for detail transfer during PBR generation.\n","properties":{"enabled":{"type":["boolean","null"],"default":true,"description":"Whether detail transfer is enabled. An omitted field, and an\nexplicit `null`, both enable detail transfer.\n"}}},"PublicPBROutput2D":{"type":"object","additionalProperties":false,"description":"Options for 2D output. All fields are optional; omitted fields\nuse system defaults.\n","properties":{"orm":{"$ref":"#/components/schemas/PublicPBROutput2DORM"}}},"PublicPBROutput2DORM":{"type":"object","additionalProperties":false,"description":"Settings for an occlusion/roughness/metallic (ORM) combined 2D output\nmap.\n\nThe API accepts this option, but it currently has no effect. Results do\nnot include an ORM map.\n","properties":{"enabled":{"type":["boolean","null"],"default":false,"description":"Whether to request the ORM map. An omitted field and an explicit\n`null` both use `false`. This option currently has no effect.\n"},"channel_order":{"type":"string","enum":["ORM","RMO","MRO"],"default":"ORM","description":"Channel order for the combined map. An omitted field packs the\nchannels in the `ORM` order.\n\nPossible enum values:\n- `ORM`: occlusion, roughness, metallic.\n- `RMO`: roughness, metallic, occlusion.\n- `MRO`: metallic, roughness, occlusion.\n"}}},"PBRExecutionResponse":{"type":"object","required":["execution_id","status","created_at"],"description":"Body returned by `POST /pbr/execute/*` (202) and\n`GET /pbr/executions/{execution_id}` (200). It describes one execution and\nis not a batch envelope: there is no batch ID, no counts, and no items\narray.\n\nOn submission every result field is empty, because nothing has finished\nyet. `asset_id` is the source asset you submitted, so a failed execution is\nalways traceable to its input. `pbr_material_assets` maps each\n`\"<slot>/<pass>\"` to its generated texture asset and a ready-to-use\n`download_url`, and `usdz` is the textured 3D model of a succeeded mesh\nexecution. `thumbnail_url` is a preview image, always present on a\nsucceeded mesh execution, and best-effort on a succeeded image execution.\n`error` is set only when the execution failed.\n","properties":{"execution_id":{"type":"string","description":"Unique identifier of the execution. Poll it at\n`GET /pbr/executions/{execution_id}`.\n"},"asset_id":{"type":"string","description":"The ID of the source asset that was submitted.\n"},"status":{"$ref":"#/components/schemas/ExecutionStatus"},"thumbnail_url":{"type":"string","format":"uri","description":"URL of a preview image. Always present on a succeeded mesh execution;\nbest-effort on a succeeded image execution.\n"},"thumbnail_url_expires_at":{"$ref":"#/components/schemas/EpochMillis"},"pbr_material_assets":{"type":"object","description":"Map from `\"<slot>/<pass>\"` to the generated texture asset. Only present\non a succeeded execution. `\"<slot>/normal\"` is an example of a key.\nThe map can hold other keys, and a key can be absent. Check that a key\nexists before you read it. When a 3D mesh to PBR job returns a normal\nmap, the map uses OpenGL tangent space. Do not assume a space for the\nnormal map of an image to PBR job.\n","additionalProperties":{"$ref":"#/components/schemas/BatchMaterialAssetBody"}},"usdz":{"allOf":[{"$ref":"#/components/schemas/BatchMaterialAssetBody"}],"description":"The `material.usdz` of the execution: one self-contained file that\ncarries the 3D model and its generated textures. A succeeded mesh\nexecution carries it, and an image execution carries none. An\nexecution that finished some time ago may carry none.\n"},"error":{"$ref":"#/components/schemas/ExecutionErrorBody"},"created_at":{"$ref":"#/components/schemas/EpochMillis"},"completed_at":{"$ref":"#/components/schemas/NullableEpochMillis"}}},"ExecutionStatus":{"type":"string","description":"Status of a single item within a batch.\n\nPossible enum values:\n- `QUEUED`: the item is waiting to start.\n- `RUNNING`: the item is being processed.\n- `SUCCEEDED`: the item completed successfully.\n- `PARTIAL_SUCCEEDED`: the item completed but some optional outputs are\n  missing.\n- `FAILED`: the item failed. See the `error` field for details.\n- `CANCELED`: the item stopped without producing a result, and any\n  credits held for it are returned. Submit the request again to retry\n  it.\n","enum":["QUEUED","RUNNING","SUCCEEDED","PARTIAL_SUCCEEDED","FAILED","CANCELED"]},"EpochMillis":{"type":"integer","format":"int64","description":"Time represented as milliseconds since the Unix epoch.\n"},"BatchMaterialAssetBody":{"type":"object","required":["asset_id"],"description":"One generated PBR result asset within a batch item. `download_url` is a\nready-to-use link that expires at `expires_at`.\n","properties":{"asset_id":{"type":"string","description":"ID of the generated asset.\n"},"download_url":{"type":"string","format":"uri","description":"Presigned URL for downloading the generated asset. Valid until\n`expires_at`.\n"},"expires_at":{"$ref":"#/components/schemas/EpochMillis"}}},"ExecutionErrorBody":{"type":"object","description":"Failure detail for a failed batch item, and for a failed run that\n`GET /mcp/executions/{execution_id}` reads. The `code` is a stable,\nmachine-readable class drawn from a closed enum so clients can branch on\nthe failure kind. The `message` carries human-readable detail. Only\n`VALIDATION_FAILED` is permanent; do not retry the same input. The other\ncodes are generally retryable.\n","required":["code","message"],"properties":{"code":{"type":"string","enum":["VALIDATION_FAILED","PIPELINE_ERROR","TIMEOUT","INTERNAL"],"description":"Machine-readable class of the failure.\n\nPossible enum values:\n- `VALIDATION_FAILED`: the input is unsuitable or invalid. This is a\n  permanent failure; retrying the same input will not succeed. It\n  covers a failure that you can fix with a change of the input. For\n  example, a 3D asset edit of a model that the edit does not support\n  yet, such as a model with 2 or more colour textures, fails with this\n  code and a message that names the cause. An upload status read uses\n  the same word for a file that the API could not check. That case is\n  retryable (see `ErrorCode`).\n- `PIPELINE_ERROR`: PBR generation started but failed.\n  Generally retryable.\n- `TIMEOUT`: the item exceeded its time budget. Generally retryable.\n- `INTERNAL`: an unexpected server error. Generally retryable.\n"},"message":{"type":"string","description":"Human-readable explanation of the failure. The message can include\nnames from the file that you uploaded, such as a material name.\n"}}},"NullableEpochMillis":{"type":["integer","null"],"format":"int64","description":"Time represented as milliseconds since the Unix epoch, or `null` when the\nfield has not been set.\n"},"Error":{"type":"object","required":["error"],"description":"Canonical error envelope. The error payload is nested under the `error`\nkey.\n","properties":{"error":{"$ref":"#/components/schemas/ErrorBody"}}},"ErrorBody":{"type":"object","description":"The body of an error response. The `code` is a stable value clients can\nbranch on; the `message` is a human-readable explanation.\n","required":["code","message"],"properties":{"code":{"$ref":"#/components/schemas/ErrorCode"},"message":{"type":"string","description":"Human-readable description of the error.\n"},"details":{"type":["object","null"],"description":"Additional structured information about the error, or `null` if no\nextra details are available.\n","additionalProperties":true}}},"ErrorCode":{"type":"string","description":"Machine-readable error code. The enum contains every code that this API\ncan return.\n\nPossible enum values:\n- `INVALID_REQUEST`: the request body or a query parameter failed validation.\n- `MALFORMED_UUID`: a path parameter that should be a UUID is not one.\n- `UNAUTHORIZED`: the request could not be authenticated.\n- `API_KEY_MISSING`: no API key was supplied.\n- `API_KEY_INVALID`: the supplied API key does not match any known key.\n- `API_KEY_REVOKED`: the supplied API key has been revoked.\n- `PLAN_FEATURE_REQUIRED`: the account plan does not include a required feature.\n- `SUBSCRIPTION_FEATURE_REQUIRED`: a new Marketplace purchase requires a paid plan or assigned seat.\n- `PRICE_CHANGED`: the selected price differs from the price in the request.\n- `VERSION_CHANGED`: the selected Marketplace version differs from the requested version.\n- `IDEMPOTENCY_CONFLICT`: the same key was used for a different purchase request.\n- `DOWNLOAD_UNAVAILABLE`: the retained private content is temporarily unavailable.\n- `CATALOGUE_CHANGED`: the catalogue changed during pagination; restart the query.\n- `LISTING_NOT_FOUND`: the selected listing is absent or delisted.\n- `MARKETPLACE_DOWNLOAD_ONLY`: Marketplace content supports download and cannot enter generation.\n- `INSUFFICIENT_CREDITS`: the account does not have enough credits for this request.\n- `QUOTA_EXCEEDED`: a usage quota for the account has been exceeded.\n- `FILE_TOO_LARGE`: an uploaded or referenced file exceeds the allowed size.\n- `UPLOAD_TIMEOUT`: an image search upload exceeded 60 seconds. Try a smaller file or a faster connection.\n- `INVALID_CONTENT_TYPE`: a file's content type does not match its declared or expected type.\n- `UNSUPPORTED_ASSET_FORMAT`: a file's extension is not one of the supported asset formats.\n- `FORMAT_NOT_AVAILABLE`: the requested downloadable format does not exist for this asset.\n- `INVALID_FBX_ZIP_STRUCTURE`: a `.zip` upload is not a valid FBX bundle. The error message says why.\n- `IMAGE_RESOLUTION_EXCEEDED`: an image is larger than 4096 by 4096 pixels, or a texture in a mesh file is larger than 8192 by 8192 pixels.\n- `VALIDATION_FAILED`: the API could not check the uploaded file. Upload it again. This is the `error.code` of an upload status read. A failed batch item or run uses the same word with another meaning (see `ExecutionErrorBody`).\n- `UPLOAD_EXPIRED`: the upload did not finish within 1 hour. Upload the file again.\n- `MESH_REQUIREMENTS_NOT_MET`: the mesh file has no polygon geometry, no base-color texture, no UV coordinates, or it names a texture that the upload does not hold.\n- `COLLECTION_NOT_FOUND`: the collection does not exist in your account.\n- `PROJECT_NOT_FOUND`: the project does not exist, or another account owns it.\n- `GRAPH_NOT_FOUND`: the graph does not exist in this project.\n- `NODE_NOT_FOUND`: the node does not exist in this graph.\n- `GROUP_NOT_FOUND`: the group does not exist in this graph.\n- `PROJECT_NODE_LIMIT_EXCEEDED`: the project already holds as many nodes as it may hold.\n- `CONFLICT`: the request conflicts with the current state, so the server made no change.\n- `ASSET_NOT_FOUND`: the asset does not exist or does not belong to the account.\n- `ASSET_IN_USE`: an active execution uses a component, or another PBR execution shares a source or target component.\n- `BATCH_TOO_LARGE`: the batch contains more items than the endpoint allows.\n- `CONCURRENT_EXECUTION_LIMIT_EXCEEDED`: the account has reached its limit on simultaneous submissions.\n- `PBR_FAILURE_CAP_EXCEEDED`: the account has too many recent failed PBR runs; new submissions are temporarily blocked.\n- `EXECUTION_NOT_FOUND`: the execution or batch does not exist or does not belong to the account.\n- `EXECUTION_IN_PROGRESS`: the request conflicts with an execution that is still running.\n- `RATE_LIMITED`: the API key's rate limit was exceeded.\n- `SERVICE_UNAVAILABLE`: the requested operation is temporarily unavailable; retry later.\n- `MAINTENANCE_MODE`: the API is temporarily unavailable for maintenance.\n- `INTERNAL_SERVER_ERROR`: an unexpected server error occurred.\n- `WORKFLOW_CREATION_FAILED`: the API could not start the requested generation.\n- `WORKFLOW_API_ERROR`: generation stopped because of an unexpected error.\n","enum":["INVALID_REQUEST","MALFORMED_UUID","UNAUTHORIZED","API_KEY_MISSING","API_KEY_INVALID","API_KEY_REVOKED","PLAN_FEATURE_REQUIRED","SUBSCRIPTION_FEATURE_REQUIRED","PRICE_CHANGED","VERSION_CHANGED","IDEMPOTENCY_CONFLICT","DOWNLOAD_UNAVAILABLE","CATALOGUE_CHANGED","LISTING_NOT_FOUND","MARKETPLACE_DOWNLOAD_ONLY","INSUFFICIENT_CREDITS","QUOTA_EXCEEDED","FILE_TOO_LARGE","UPLOAD_TIMEOUT","INVALID_CONTENT_TYPE","UNSUPPORTED_ASSET_FORMAT","FORMAT_NOT_AVAILABLE","INVALID_FBX_ZIP_STRUCTURE","IMAGE_RESOLUTION_EXCEEDED","VALIDATION_FAILED","UPLOAD_EXPIRED","MESH_REQUIREMENTS_NOT_MET","COLLECTION_NOT_FOUND","PROJECT_NOT_FOUND","GRAPH_NOT_FOUND","NODE_NOT_FOUND","GROUP_NOT_FOUND","PROJECT_NODE_LIMIT_EXCEEDED","CONFLICT","ASSET_NOT_FOUND","ASSET_IN_USE","BATCH_TOO_LARGE","CONCURRENT_EXECUTION_LIMIT_EXCEEDED","PBR_FAILURE_CAP_EXCEEDED","EXECUTION_NOT_FOUND","EXECUTION_IN_PROGRESS","RATE_LIMITED","SERVICE_UNAVAILABLE","MAINTENANCE_MODE","INTERNAL_SERVER_ERROR","WORKFLOW_CREATION_FAILED","WORKFLOW_API_ERROR"]},"PlanFeatureRequiredError":{"type":"object","required":["error"],"description":"The error envelope for a missing subscription feature.","properties":{"error":{"type":"object","required":["code","message","details"],"description":"The missing-feature error body.","properties":{"code":{"type":"string","const":"PLAN_FEATURE_REQUIRED","description":"Stable error code for a missing subscription feature."},"message":{"type":"string","description":"Human-readable description of the error."},"details":{"$ref":"#/components/schemas/PlanFeatureRequiredErrorDetails"}}}}},"PlanFeatureRequiredErrorDetails":{"type":"object","required":["feature","upgrade_plan_codes"],"description":"The missing feature and the active plans that provide it.","properties":{"feature":{"$ref":"#/components/schemas/SubscriptionFeature"},"upgrade_plan_codes":{"type":"array","description":"Active plans available for purchase that provide the missing\nfeature. The array uses plan class and opaque code order.\n","items":{"$ref":"#/components/schemas/SubscriptionPlanCode"}}}},"SubscriptionFeature":{"type":"string","description":"Product capability available to the account.\n\nPossible enum values:\n- `commercial_license`: commercial-use rights.\n- `api_pbr`: PBR generation through the Public Platform API.\n- `api_asset_download`: asset downloads through the Public Platform API.\n- `custom_polycount`: custom polygon counts for 3D generation.\n- `image_edit`: image editing.\n- `multiview`: multiview generation.\n- `asset_download`: asset downloads in the Marso web app.\n- `api_access`: access to the Public Platform API.\n- `scene_generation`: scene generation and multi-asset runs in the Marso web app.\n- `application_apps`: the Sim Ready and Game Ready apps.\n","enum":["commercial_license","api_pbr","api_asset_download","custom_polycount","image_edit","multiview","asset_download","api_access","scene_generation","application_apps"]},"SubscriptionPlanCode":{"type":"string","pattern":"^plan_[A-Za-z0-9]{7,64}$","description":"Opaque and immutable code for a subscription plan. A plan display name\ncan change. The code starts with `plan_`, followed by 7 to 64 ASCII\nletters or digits.\n"}},"responses":{"Unauthorized":{"description":"The API key is missing, invalid, or revoked. Possible error codes:\n`API_KEY_MISSING`, `API_KEY_INVALID`, `API_KEY_REVOKED`, or `UNAUTHORIZED`.\n","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"PlanFeatureRequired":{"description":"The account does not have the plan feature required by this operation\n(`PLAN_FEATURE_REQUIRED`). The Free plan lacks `api_access`, so every\noperation refuses a Free account with this response. An account that\nlacks `api_pbr` or `api_asset_download` also gets it on the routes that\nneed those features.\n","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlanFeatureRequiredError"}}}},"RateLimitedOrCapped":{"description":"The request could not be accepted because of a rate or concurrency limit.\nPossible error codes:\n\n- `RATE_LIMITED`: the API-key rate limit was exceeded.\n- `CONCURRENT_EXECUTION_LIMIT_EXCEEDED`: a single submission exceeded\n  the concurrency limit.\n- `PBR_FAILURE_CAP_EXCEEDED`: too many consecutive failures; check the\n  input before retrying.\n","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"paths":{"/pbr/execute/mesh":{"post":{"operationId":"createMeshPBRExecution","tags":["pbr"],"summary":"Generate PBR materials from one 3D mesh.","description":"Submits one ready mesh asset for 3D mesh to PBR generation. The asset must\nbe a `ready` mesh uploaded with `POST /uploads` or `POST /batch/uploads`.\n\nThe request returns immediately with an `execution_id` (`202`). Poll\n`GET /pbr/executions/{execution_id}` for the status and, once the run\nfinishes, the generated material maps and their download URLs. Credits are\nreserved when you submit and refunded automatically if the run fails, so\nyou are only charged for a successful result.\n\nKeep the mesh within the published limits that `POST /uploads` lists:\n1000 parts, 64 materials and 16 different colour textures. A texture is\n8192 pixels or smaller on its longest side. A mesh above a limit can fail.\n\nUse `POST /batch/pbr/mesh` instead when you have several meshes to submit\ntogether.\n","requestBody":{"required":true,"description":"The mesh asset to submit, and the options for the run.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreatePBRMeshExecutionRequest"}}}},"responses":{"202":{"description":"The execution was created and queued.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PBRExecutionResponse"}}}},"400":{"description":"The API rejects a request without `asset_id`. It also rejects\nunsupported options, a non-mesh asset, or an asset outside the\n`ready` state (`INVALID_REQUEST`).\n","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/PlanFeatureRequired"},"404":{"description":"The asset does not exist or does not belong to the authenticated\naccount (`ASSET_NOT_FOUND`).\n","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"$ref":"#/components/responses/RateLimitedOrCapped"}}}}}}
```

## Generate PBR materials from one image.

> Submits one ready image asset for image to PBR generation. The image\
> counterpart of \`POST /pbr/execute/mesh\`. It takes \`detail\_transfer\` and\
> the image-only \`tileable\` option, but not \`output\_2d\`. Image results do\
> not include a USDZ file.\
> \
> The request returns immediately with an \`execution\_id\` (\`202\`). Poll\
> \`GET /pbr/executions/{execution\_id}\` for the status and the generated\
> material maps. Credits are reserved when you submit and refunded\
> automatically if the run fails.\
> \
> Use \`POST /batch/pbr/image\` instead when you have several images to submit\
> together.<br>

```json
{"openapi":"3.1.0","info":{"title":"Marso Studio API","version":"0.0.1"},"tags":[{"name":"pbr","description":"Batches group one or more PBR generation jobs together. Use these endpoints\nto submit 3D mesh to PBR or image to PBR batches, poll a batch for progress,\nand list recent batches.\n"}],"servers":[{"url":"https://api.marso.app/api/public/v1/","description":"Production server."}],"security":[{"apiKey":[]}],"components":{"securitySchemes":{"apiKey":{"type":"http","scheme":"bearer","description":"API key created in the Marso Studio dashboard. The value has the form\n`mxr_live_<entropy>` and is sent as a Bearer token in the `Authorization`\nheader.\n"}},"schemas":{"CreatePBRImageExecutionRequest":{"type":"object","required":["asset_id"],"description":"Request body for `POST /pbr/execute/image`. Submit one ready image asset\nfor PBR generation. Image PBR options include `tileable`, and do not\ninclude `output_2d`.\n","properties":{"asset_id":{"type":"string","format":"uuid","description":"The ID of the image asset to process. Upload it with\n`POST /uploads` or `POST /batch/uploads` first. Its status must be\n`ready`.\n"},"options":{"$ref":"#/components/schemas/PublicPBRImageOptions"}}},"PublicPBRImageOptions":{"type":"object","additionalProperties":false,"description":"Tuning options for an image PBR run. Every field is optional, and an\nomitted field uses the system default.\n\nImage options include `detail_transfer` and `tileable`. Image results\ndo not include a USDZ file. Unknown fields return a `400` response.\n","properties":{"detail_transfer":{"$ref":"#/components/schemas/PublicPBRDetailTransfer"},"tileable":{"type":["boolean","null"],"default":false,"description":"Whether the image is a seamless texture, one that repeats with no\nvisible seam when you tile it. Set it to `true` only for such a\ntexture, not for a photograph. The generated maps then also tile\nwithout a seam at their edges. An omitted field and an explicit\n`null` both use `false`.\n"}}},"PublicPBRDetailTransfer":{"type":"object","additionalProperties":false,"description":"Settings for detail transfer during PBR generation.\n","properties":{"enabled":{"type":["boolean","null"],"default":true,"description":"Whether detail transfer is enabled. An omitted field, and an\nexplicit `null`, both enable detail transfer.\n"}}},"PBRExecutionResponse":{"type":"object","required":["execution_id","status","created_at"],"description":"Body returned by `POST /pbr/execute/*` (202) and\n`GET /pbr/executions/{execution_id}` (200). It describes one execution and\nis not a batch envelope: there is no batch ID, no counts, and no items\narray.\n\nOn submission every result field is empty, because nothing has finished\nyet. `asset_id` is the source asset you submitted, so a failed execution is\nalways traceable to its input. `pbr_material_assets` maps each\n`\"<slot>/<pass>\"` to its generated texture asset and a ready-to-use\n`download_url`, and `usdz` is the textured 3D model of a succeeded mesh\nexecution. `thumbnail_url` is a preview image, always present on a\nsucceeded mesh execution, and best-effort on a succeeded image execution.\n`error` is set only when the execution failed.\n","properties":{"execution_id":{"type":"string","description":"Unique identifier of the execution. Poll it at\n`GET /pbr/executions/{execution_id}`.\n"},"asset_id":{"type":"string","description":"The ID of the source asset that was submitted.\n"},"status":{"$ref":"#/components/schemas/ExecutionStatus"},"thumbnail_url":{"type":"string","format":"uri","description":"URL of a preview image. Always present on a succeeded mesh execution;\nbest-effort on a succeeded image execution.\n"},"thumbnail_url_expires_at":{"$ref":"#/components/schemas/EpochMillis"},"pbr_material_assets":{"type":"object","description":"Map from `\"<slot>/<pass>\"` to the generated texture asset. Only present\non a succeeded execution. `\"<slot>/normal\"` is an example of a key.\nThe map can hold other keys, and a key can be absent. Check that a key\nexists before you read it. When a 3D mesh to PBR job returns a normal\nmap, the map uses OpenGL tangent space. Do not assume a space for the\nnormal map of an image to PBR job.\n","additionalProperties":{"$ref":"#/components/schemas/BatchMaterialAssetBody"}},"usdz":{"allOf":[{"$ref":"#/components/schemas/BatchMaterialAssetBody"}],"description":"The `material.usdz` of the execution: one self-contained file that\ncarries the 3D model and its generated textures. A succeeded mesh\nexecution carries it, and an image execution carries none. An\nexecution that finished some time ago may carry none.\n"},"error":{"$ref":"#/components/schemas/ExecutionErrorBody"},"created_at":{"$ref":"#/components/schemas/EpochMillis"},"completed_at":{"$ref":"#/components/schemas/NullableEpochMillis"}}},"ExecutionStatus":{"type":"string","description":"Status of a single item within a batch.\n\nPossible enum values:\n- `QUEUED`: the item is waiting to start.\n- `RUNNING`: the item is being processed.\n- `SUCCEEDED`: the item completed successfully.\n- `PARTIAL_SUCCEEDED`: the item completed but some optional outputs are\n  missing.\n- `FAILED`: the item failed. See the `error` field for details.\n- `CANCELED`: the item stopped without producing a result, and any\n  credits held for it are returned. Submit the request again to retry\n  it.\n","enum":["QUEUED","RUNNING","SUCCEEDED","PARTIAL_SUCCEEDED","FAILED","CANCELED"]},"EpochMillis":{"type":"integer","format":"int64","description":"Time represented as milliseconds since the Unix epoch.\n"},"BatchMaterialAssetBody":{"type":"object","required":["asset_id"],"description":"One generated PBR result asset within a batch item. `download_url` is a\nready-to-use link that expires at `expires_at`.\n","properties":{"asset_id":{"type":"string","description":"ID of the generated asset.\n"},"download_url":{"type":"string","format":"uri","description":"Presigned URL for downloading the generated asset. Valid until\n`expires_at`.\n"},"expires_at":{"$ref":"#/components/schemas/EpochMillis"}}},"ExecutionErrorBody":{"type":"object","description":"Failure detail for a failed batch item, and for a failed run that\n`GET /mcp/executions/{execution_id}` reads. The `code` is a stable,\nmachine-readable class drawn from a closed enum so clients can branch on\nthe failure kind. The `message` carries human-readable detail. Only\n`VALIDATION_FAILED` is permanent; do not retry the same input. The other\ncodes are generally retryable.\n","required":["code","message"],"properties":{"code":{"type":"string","enum":["VALIDATION_FAILED","PIPELINE_ERROR","TIMEOUT","INTERNAL"],"description":"Machine-readable class of the failure.\n\nPossible enum values:\n- `VALIDATION_FAILED`: the input is unsuitable or invalid. This is a\n  permanent failure; retrying the same input will not succeed. It\n  covers a failure that you can fix with a change of the input. For\n  example, a 3D asset edit of a model that the edit does not support\n  yet, such as a model with 2 or more colour textures, fails with this\n  code and a message that names the cause. An upload status read uses\n  the same word for a file that the API could not check. That case is\n  retryable (see `ErrorCode`).\n- `PIPELINE_ERROR`: PBR generation started but failed.\n  Generally retryable.\n- `TIMEOUT`: the item exceeded its time budget. Generally retryable.\n- `INTERNAL`: an unexpected server error. Generally retryable.\n"},"message":{"type":"string","description":"Human-readable explanation of the failure. The message can include\nnames from the file that you uploaded, such as a material name.\n"}}},"NullableEpochMillis":{"type":["integer","null"],"format":"int64","description":"Time represented as milliseconds since the Unix epoch, or `null` when the\nfield has not been set.\n"},"Error":{"type":"object","required":["error"],"description":"Canonical error envelope. The error payload is nested under the `error`\nkey.\n","properties":{"error":{"$ref":"#/components/schemas/ErrorBody"}}},"ErrorBody":{"type":"object","description":"The body of an error response. The `code` is a stable value clients can\nbranch on; the `message` is a human-readable explanation.\n","required":["code","message"],"properties":{"code":{"$ref":"#/components/schemas/ErrorCode"},"message":{"type":"string","description":"Human-readable description of the error.\n"},"details":{"type":["object","null"],"description":"Additional structured information about the error, or `null` if no\nextra details are available.\n","additionalProperties":true}}},"ErrorCode":{"type":"string","description":"Machine-readable error code. The enum contains every code that this API\ncan return.\n\nPossible enum values:\n- `INVALID_REQUEST`: the request body or a query parameter failed validation.\n- `MALFORMED_UUID`: a path parameter that should be a UUID is not one.\n- `UNAUTHORIZED`: the request could not be authenticated.\n- `API_KEY_MISSING`: no API key was supplied.\n- `API_KEY_INVALID`: the supplied API key does not match any known key.\n- `API_KEY_REVOKED`: the supplied API key has been revoked.\n- `PLAN_FEATURE_REQUIRED`: the account plan does not include a required feature.\n- `SUBSCRIPTION_FEATURE_REQUIRED`: a new Marketplace purchase requires a paid plan or assigned seat.\n- `PRICE_CHANGED`: the selected price differs from the price in the request.\n- `VERSION_CHANGED`: the selected Marketplace version differs from the requested version.\n- `IDEMPOTENCY_CONFLICT`: the same key was used for a different purchase request.\n- `DOWNLOAD_UNAVAILABLE`: the retained private content is temporarily unavailable.\n- `CATALOGUE_CHANGED`: the catalogue changed during pagination; restart the query.\n- `LISTING_NOT_FOUND`: the selected listing is absent or delisted.\n- `MARKETPLACE_DOWNLOAD_ONLY`: Marketplace content supports download and cannot enter generation.\n- `INSUFFICIENT_CREDITS`: the account does not have enough credits for this request.\n- `QUOTA_EXCEEDED`: a usage quota for the account has been exceeded.\n- `FILE_TOO_LARGE`: an uploaded or referenced file exceeds the allowed size.\n- `UPLOAD_TIMEOUT`: an image search upload exceeded 60 seconds. Try a smaller file or a faster connection.\n- `INVALID_CONTENT_TYPE`: a file's content type does not match its declared or expected type.\n- `UNSUPPORTED_ASSET_FORMAT`: a file's extension is not one of the supported asset formats.\n- `FORMAT_NOT_AVAILABLE`: the requested downloadable format does not exist for this asset.\n- `INVALID_FBX_ZIP_STRUCTURE`: a `.zip` upload is not a valid FBX bundle. The error message says why.\n- `IMAGE_RESOLUTION_EXCEEDED`: an image is larger than 4096 by 4096 pixels, or a texture in a mesh file is larger than 8192 by 8192 pixels.\n- `VALIDATION_FAILED`: the API could not check the uploaded file. Upload it again. This is the `error.code` of an upload status read. A failed batch item or run uses the same word with another meaning (see `ExecutionErrorBody`).\n- `UPLOAD_EXPIRED`: the upload did not finish within 1 hour. Upload the file again.\n- `MESH_REQUIREMENTS_NOT_MET`: the mesh file has no polygon geometry, no base-color texture, no UV coordinates, or it names a texture that the upload does not hold.\n- `COLLECTION_NOT_FOUND`: the collection does not exist in your account.\n- `PROJECT_NOT_FOUND`: the project does not exist, or another account owns it.\n- `GRAPH_NOT_FOUND`: the graph does not exist in this project.\n- `NODE_NOT_FOUND`: the node does not exist in this graph.\n- `GROUP_NOT_FOUND`: the group does not exist in this graph.\n- `PROJECT_NODE_LIMIT_EXCEEDED`: the project already holds as many nodes as it may hold.\n- `CONFLICT`: the request conflicts with the current state, so the server made no change.\n- `ASSET_NOT_FOUND`: the asset does not exist or does not belong to the account.\n- `ASSET_IN_USE`: an active execution uses a component, or another PBR execution shares a source or target component.\n- `BATCH_TOO_LARGE`: the batch contains more items than the endpoint allows.\n- `CONCURRENT_EXECUTION_LIMIT_EXCEEDED`: the account has reached its limit on simultaneous submissions.\n- `PBR_FAILURE_CAP_EXCEEDED`: the account has too many recent failed PBR runs; new submissions are temporarily blocked.\n- `EXECUTION_NOT_FOUND`: the execution or batch does not exist or does not belong to the account.\n- `EXECUTION_IN_PROGRESS`: the request conflicts with an execution that is still running.\n- `RATE_LIMITED`: the API key's rate limit was exceeded.\n- `SERVICE_UNAVAILABLE`: the requested operation is temporarily unavailable; retry later.\n- `MAINTENANCE_MODE`: the API is temporarily unavailable for maintenance.\n- `INTERNAL_SERVER_ERROR`: an unexpected server error occurred.\n- `WORKFLOW_CREATION_FAILED`: the API could not start the requested generation.\n- `WORKFLOW_API_ERROR`: generation stopped because of an unexpected error.\n","enum":["INVALID_REQUEST","MALFORMED_UUID","UNAUTHORIZED","API_KEY_MISSING","API_KEY_INVALID","API_KEY_REVOKED","PLAN_FEATURE_REQUIRED","SUBSCRIPTION_FEATURE_REQUIRED","PRICE_CHANGED","VERSION_CHANGED","IDEMPOTENCY_CONFLICT","DOWNLOAD_UNAVAILABLE","CATALOGUE_CHANGED","LISTING_NOT_FOUND","MARKETPLACE_DOWNLOAD_ONLY","INSUFFICIENT_CREDITS","QUOTA_EXCEEDED","FILE_TOO_LARGE","UPLOAD_TIMEOUT","INVALID_CONTENT_TYPE","UNSUPPORTED_ASSET_FORMAT","FORMAT_NOT_AVAILABLE","INVALID_FBX_ZIP_STRUCTURE","IMAGE_RESOLUTION_EXCEEDED","VALIDATION_FAILED","UPLOAD_EXPIRED","MESH_REQUIREMENTS_NOT_MET","COLLECTION_NOT_FOUND","PROJECT_NOT_FOUND","GRAPH_NOT_FOUND","NODE_NOT_FOUND","GROUP_NOT_FOUND","PROJECT_NODE_LIMIT_EXCEEDED","CONFLICT","ASSET_NOT_FOUND","ASSET_IN_USE","BATCH_TOO_LARGE","CONCURRENT_EXECUTION_LIMIT_EXCEEDED","PBR_FAILURE_CAP_EXCEEDED","EXECUTION_NOT_FOUND","EXECUTION_IN_PROGRESS","RATE_LIMITED","SERVICE_UNAVAILABLE","MAINTENANCE_MODE","INTERNAL_SERVER_ERROR","WORKFLOW_CREATION_FAILED","WORKFLOW_API_ERROR"]},"PlanFeatureRequiredError":{"type":"object","required":["error"],"description":"The error envelope for a missing subscription feature.","properties":{"error":{"type":"object","required":["code","message","details"],"description":"The missing-feature error body.","properties":{"code":{"type":"string","const":"PLAN_FEATURE_REQUIRED","description":"Stable error code for a missing subscription feature."},"message":{"type":"string","description":"Human-readable description of the error."},"details":{"$ref":"#/components/schemas/PlanFeatureRequiredErrorDetails"}}}}},"PlanFeatureRequiredErrorDetails":{"type":"object","required":["feature","upgrade_plan_codes"],"description":"The missing feature and the active plans that provide it.","properties":{"feature":{"$ref":"#/components/schemas/SubscriptionFeature"},"upgrade_plan_codes":{"type":"array","description":"Active plans available for purchase that provide the missing\nfeature. The array uses plan class and opaque code order.\n","items":{"$ref":"#/components/schemas/SubscriptionPlanCode"}}}},"SubscriptionFeature":{"type":"string","description":"Product capability available to the account.\n\nPossible enum values:\n- `commercial_license`: commercial-use rights.\n- `api_pbr`: PBR generation through the Public Platform API.\n- `api_asset_download`: asset downloads through the Public Platform API.\n- `custom_polycount`: custom polygon counts for 3D generation.\n- `image_edit`: image editing.\n- `multiview`: multiview generation.\n- `asset_download`: asset downloads in the Marso web app.\n- `api_access`: access to the Public Platform API.\n- `scene_generation`: scene generation and multi-asset runs in the Marso web app.\n- `application_apps`: the Sim Ready and Game Ready apps.\n","enum":["commercial_license","api_pbr","api_asset_download","custom_polycount","image_edit","multiview","asset_download","api_access","scene_generation","application_apps"]},"SubscriptionPlanCode":{"type":"string","pattern":"^plan_[A-Za-z0-9]{7,64}$","description":"Opaque and immutable code for a subscription plan. A plan display name\ncan change. The code starts with `plan_`, followed by 7 to 64 ASCII\nletters or digits.\n"}},"responses":{"Unauthorized":{"description":"The API key is missing, invalid, or revoked. Possible error codes:\n`API_KEY_MISSING`, `API_KEY_INVALID`, `API_KEY_REVOKED`, or `UNAUTHORIZED`.\n","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"PlanFeatureRequired":{"description":"The account does not have the plan feature required by this operation\n(`PLAN_FEATURE_REQUIRED`). The Free plan lacks `api_access`, so every\noperation refuses a Free account with this response. An account that\nlacks `api_pbr` or `api_asset_download` also gets it on the routes that\nneed those features.\n","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlanFeatureRequiredError"}}}},"RateLimitedOrCapped":{"description":"The request could not be accepted because of a rate or concurrency limit.\nPossible error codes:\n\n- `RATE_LIMITED`: the API-key rate limit was exceeded.\n- `CONCURRENT_EXECUTION_LIMIT_EXCEEDED`: a single submission exceeded\n  the concurrency limit.\n- `PBR_FAILURE_CAP_EXCEEDED`: too many consecutive failures; check the\n  input before retrying.\n","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"paths":{"/pbr/execute/image":{"post":{"operationId":"createImagePBRExecution","tags":["pbr"],"summary":"Generate PBR materials from one image.","description":"Submits one ready image asset for image to PBR generation. The image\ncounterpart of `POST /pbr/execute/mesh`. It takes `detail_transfer` and\nthe image-only `tileable` option, but not `output_2d`. Image results do\nnot include a USDZ file.\n\nThe request returns immediately with an `execution_id` (`202`). Poll\n`GET /pbr/executions/{execution_id}` for the status and the generated\nmaterial maps. Credits are reserved when you submit and refunded\nautomatically if the run fails.\n\nUse `POST /batch/pbr/image` instead when you have several images to submit\ntogether.\n","requestBody":{"required":true,"description":"The image asset to submit, and the options for the run.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreatePBRImageExecutionRequest"}}}},"responses":{"202":{"description":"The execution was created and queued.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PBRExecutionResponse"}}}},"400":{"description":"The API rejects a request without `asset_id`. It also rejects\nunsupported options, a non-image asset, or an asset outside the\n`ready` state (`INVALID_REQUEST`).\n","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/PlanFeatureRequired"},"404":{"description":"The asset does not exist or does not belong to the authenticated\naccount (`ASSET_NOT_FOUND`).\n","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"$ref":"#/components/responses/RateLimitedOrCapped"}}}}}}
```

## Retrieve a single PBR execution.

> Returns the status of one execution submitted with\
> \`POST /pbr/execute/mesh\` or \`POST /pbr/execute/image\` and, once it\
> finishes, its generated material maps with ready-to-use download URLs, or\
> a typed error if it failed. This is the endpoint you poll after a single\
> submission.\
> \
> The status moves from \`QUEUED\` straight to its terminal value. A single\
> execution does not report \`RUNNING\`, so poll for a terminal status rather\
> than waiting for a start signal.\
> \
> This endpoint serves single submissions only. The items of a batch are\
> read with \`GET /batch/{batch\_id}\`.<br>

```json
{"openapi":"3.1.0","info":{"title":"Marso Studio API","version":"0.0.1"},"tags":[{"name":"pbr","description":"Batches group one or more PBR generation jobs together. Use these endpoints\nto submit 3D mesh to PBR or image to PBR batches, poll a batch for progress,\nand list recent batches.\n"}],"servers":[{"url":"https://api.marso.app/api/public/v1/","description":"Production server."}],"security":[{"apiKey":[]}],"components":{"securitySchemes":{"apiKey":{"type":"http","scheme":"bearer","description":"API key created in the Marso Studio dashboard. The value has the form\n`mxr_live_<entropy>` and is sent as a Bearer token in the `Authorization`\nheader.\n"}},"parameters":{"ExecutionId":{"name":"execution_id","in":"path","required":true,"description":"Unique identifier of the execution.","schema":{"type":"string","format":"uuid"}}},"schemas":{"PBRExecutionResponse":{"type":"object","required":["execution_id","status","created_at"],"description":"Body returned by `POST /pbr/execute/*` (202) and\n`GET /pbr/executions/{execution_id}` (200). It describes one execution and\nis not a batch envelope: there is no batch ID, no counts, and no items\narray.\n\nOn submission every result field is empty, because nothing has finished\nyet. `asset_id` is the source asset you submitted, so a failed execution is\nalways traceable to its input. `pbr_material_assets` maps each\n`\"<slot>/<pass>\"` to its generated texture asset and a ready-to-use\n`download_url`, and `usdz` is the textured 3D model of a succeeded mesh\nexecution. `thumbnail_url` is a preview image, always present on a\nsucceeded mesh execution, and best-effort on a succeeded image execution.\n`error` is set only when the execution failed.\n","properties":{"execution_id":{"type":"string","description":"Unique identifier of the execution. Poll it at\n`GET /pbr/executions/{execution_id}`.\n"},"asset_id":{"type":"string","description":"The ID of the source asset that was submitted.\n"},"status":{"$ref":"#/components/schemas/ExecutionStatus"},"thumbnail_url":{"type":"string","format":"uri","description":"URL of a preview image. Always present on a succeeded mesh execution;\nbest-effort on a succeeded image execution.\n"},"thumbnail_url_expires_at":{"$ref":"#/components/schemas/EpochMillis"},"pbr_material_assets":{"type":"object","description":"Map from `\"<slot>/<pass>\"` to the generated texture asset. Only present\non a succeeded execution. `\"<slot>/normal\"` is an example of a key.\nThe map can hold other keys, and a key can be absent. Check that a key\nexists before you read it. When a 3D mesh to PBR job returns a normal\nmap, the map uses OpenGL tangent space. Do not assume a space for the\nnormal map of an image to PBR job.\n","additionalProperties":{"$ref":"#/components/schemas/BatchMaterialAssetBody"}},"usdz":{"allOf":[{"$ref":"#/components/schemas/BatchMaterialAssetBody"}],"description":"The `material.usdz` of the execution: one self-contained file that\ncarries the 3D model and its generated textures. A succeeded mesh\nexecution carries it, and an image execution carries none. An\nexecution that finished some time ago may carry none.\n"},"error":{"$ref":"#/components/schemas/ExecutionErrorBody"},"created_at":{"$ref":"#/components/schemas/EpochMillis"},"completed_at":{"$ref":"#/components/schemas/NullableEpochMillis"}}},"ExecutionStatus":{"type":"string","description":"Status of a single item within a batch.\n\nPossible enum values:\n- `QUEUED`: the item is waiting to start.\n- `RUNNING`: the item is being processed.\n- `SUCCEEDED`: the item completed successfully.\n- `PARTIAL_SUCCEEDED`: the item completed but some optional outputs are\n  missing.\n- `FAILED`: the item failed. See the `error` field for details.\n- `CANCELED`: the item stopped without producing a result, and any\n  credits held for it are returned. Submit the request again to retry\n  it.\n","enum":["QUEUED","RUNNING","SUCCEEDED","PARTIAL_SUCCEEDED","FAILED","CANCELED"]},"EpochMillis":{"type":"integer","format":"int64","description":"Time represented as milliseconds since the Unix epoch.\n"},"BatchMaterialAssetBody":{"type":"object","required":["asset_id"],"description":"One generated PBR result asset within a batch item. `download_url` is a\nready-to-use link that expires at `expires_at`.\n","properties":{"asset_id":{"type":"string","description":"ID of the generated asset.\n"},"download_url":{"type":"string","format":"uri","description":"Presigned URL for downloading the generated asset. Valid until\n`expires_at`.\n"},"expires_at":{"$ref":"#/components/schemas/EpochMillis"}}},"ExecutionErrorBody":{"type":"object","description":"Failure detail for a failed batch item, and for a failed run that\n`GET /mcp/executions/{execution_id}` reads. The `code` is a stable,\nmachine-readable class drawn from a closed enum so clients can branch on\nthe failure kind. The `message` carries human-readable detail. Only\n`VALIDATION_FAILED` is permanent; do not retry the same input. The other\ncodes are generally retryable.\n","required":["code","message"],"properties":{"code":{"type":"string","enum":["VALIDATION_FAILED","PIPELINE_ERROR","TIMEOUT","INTERNAL"],"description":"Machine-readable class of the failure.\n\nPossible enum values:\n- `VALIDATION_FAILED`: the input is unsuitable or invalid. This is a\n  permanent failure; retrying the same input will not succeed. It\n  covers a failure that you can fix with a change of the input. For\n  example, a 3D asset edit of a model that the edit does not support\n  yet, such as a model with 2 or more colour textures, fails with this\n  code and a message that names the cause. An upload status read uses\n  the same word for a file that the API could not check. That case is\n  retryable (see `ErrorCode`).\n- `PIPELINE_ERROR`: PBR generation started but failed.\n  Generally retryable.\n- `TIMEOUT`: the item exceeded its time budget. Generally retryable.\n- `INTERNAL`: an unexpected server error. Generally retryable.\n"},"message":{"type":"string","description":"Human-readable explanation of the failure. The message can include\nnames from the file that you uploaded, such as a material name.\n"}}},"NullableEpochMillis":{"type":["integer","null"],"format":"int64","description":"Time represented as milliseconds since the Unix epoch, or `null` when the\nfield has not been set.\n"},"Error":{"type":"object","required":["error"],"description":"Canonical error envelope. The error payload is nested under the `error`\nkey.\n","properties":{"error":{"$ref":"#/components/schemas/ErrorBody"}}},"ErrorBody":{"type":"object","description":"The body of an error response. The `code` is a stable value clients can\nbranch on; the `message` is a human-readable explanation.\n","required":["code","message"],"properties":{"code":{"$ref":"#/components/schemas/ErrorCode"},"message":{"type":"string","description":"Human-readable description of the error.\n"},"details":{"type":["object","null"],"description":"Additional structured information about the error, or `null` if no\nextra details are available.\n","additionalProperties":true}}},"ErrorCode":{"type":"string","description":"Machine-readable error code. The enum contains every code that this API\ncan return.\n\nPossible enum values:\n- `INVALID_REQUEST`: the request body or a query parameter failed validation.\n- `MALFORMED_UUID`: a path parameter that should be a UUID is not one.\n- `UNAUTHORIZED`: the request could not be authenticated.\n- `API_KEY_MISSING`: no API key was supplied.\n- `API_KEY_INVALID`: the supplied API key does not match any known key.\n- `API_KEY_REVOKED`: the supplied API key has been revoked.\n- `PLAN_FEATURE_REQUIRED`: the account plan does not include a required feature.\n- `SUBSCRIPTION_FEATURE_REQUIRED`: a new Marketplace purchase requires a paid plan or assigned seat.\n- `PRICE_CHANGED`: the selected price differs from the price in the request.\n- `VERSION_CHANGED`: the selected Marketplace version differs from the requested version.\n- `IDEMPOTENCY_CONFLICT`: the same key was used for a different purchase request.\n- `DOWNLOAD_UNAVAILABLE`: the retained private content is temporarily unavailable.\n- `CATALOGUE_CHANGED`: the catalogue changed during pagination; restart the query.\n- `LISTING_NOT_FOUND`: the selected listing is absent or delisted.\n- `MARKETPLACE_DOWNLOAD_ONLY`: Marketplace content supports download and cannot enter generation.\n- `INSUFFICIENT_CREDITS`: the account does not have enough credits for this request.\n- `QUOTA_EXCEEDED`: a usage quota for the account has been exceeded.\n- `FILE_TOO_LARGE`: an uploaded or referenced file exceeds the allowed size.\n- `UPLOAD_TIMEOUT`: an image search upload exceeded 60 seconds. Try a smaller file or a faster connection.\n- `INVALID_CONTENT_TYPE`: a file's content type does not match its declared or expected type.\n- `UNSUPPORTED_ASSET_FORMAT`: a file's extension is not one of the supported asset formats.\n- `FORMAT_NOT_AVAILABLE`: the requested downloadable format does not exist for this asset.\n- `INVALID_FBX_ZIP_STRUCTURE`: a `.zip` upload is not a valid FBX bundle. The error message says why.\n- `IMAGE_RESOLUTION_EXCEEDED`: an image is larger than 4096 by 4096 pixels, or a texture in a mesh file is larger than 8192 by 8192 pixels.\n- `VALIDATION_FAILED`: the API could not check the uploaded file. Upload it again. This is the `error.code` of an upload status read. A failed batch item or run uses the same word with another meaning (see `ExecutionErrorBody`).\n- `UPLOAD_EXPIRED`: the upload did not finish within 1 hour. Upload the file again.\n- `MESH_REQUIREMENTS_NOT_MET`: the mesh file has no polygon geometry, no base-color texture, no UV coordinates, or it names a texture that the upload does not hold.\n- `COLLECTION_NOT_FOUND`: the collection does not exist in your account.\n- `PROJECT_NOT_FOUND`: the project does not exist, or another account owns it.\n- `GRAPH_NOT_FOUND`: the graph does not exist in this project.\n- `NODE_NOT_FOUND`: the node does not exist in this graph.\n- `GROUP_NOT_FOUND`: the group does not exist in this graph.\n- `PROJECT_NODE_LIMIT_EXCEEDED`: the project already holds as many nodes as it may hold.\n- `CONFLICT`: the request conflicts with the current state, so the server made no change.\n- `ASSET_NOT_FOUND`: the asset does not exist or does not belong to the account.\n- `ASSET_IN_USE`: an active execution uses a component, or another PBR execution shares a source or target component.\n- `BATCH_TOO_LARGE`: the batch contains more items than the endpoint allows.\n- `CONCURRENT_EXECUTION_LIMIT_EXCEEDED`: the account has reached its limit on simultaneous submissions.\n- `PBR_FAILURE_CAP_EXCEEDED`: the account has too many recent failed PBR runs; new submissions are temporarily blocked.\n- `EXECUTION_NOT_FOUND`: the execution or batch does not exist or does not belong to the account.\n- `EXECUTION_IN_PROGRESS`: the request conflicts with an execution that is still running.\n- `RATE_LIMITED`: the API key's rate limit was exceeded.\n- `SERVICE_UNAVAILABLE`: the requested operation is temporarily unavailable; retry later.\n- `MAINTENANCE_MODE`: the API is temporarily unavailable for maintenance.\n- `INTERNAL_SERVER_ERROR`: an unexpected server error occurred.\n- `WORKFLOW_CREATION_FAILED`: the API could not start the requested generation.\n- `WORKFLOW_API_ERROR`: generation stopped because of an unexpected error.\n","enum":["INVALID_REQUEST","MALFORMED_UUID","UNAUTHORIZED","API_KEY_MISSING","API_KEY_INVALID","API_KEY_REVOKED","PLAN_FEATURE_REQUIRED","SUBSCRIPTION_FEATURE_REQUIRED","PRICE_CHANGED","VERSION_CHANGED","IDEMPOTENCY_CONFLICT","DOWNLOAD_UNAVAILABLE","CATALOGUE_CHANGED","LISTING_NOT_FOUND","MARKETPLACE_DOWNLOAD_ONLY","INSUFFICIENT_CREDITS","QUOTA_EXCEEDED","FILE_TOO_LARGE","UPLOAD_TIMEOUT","INVALID_CONTENT_TYPE","UNSUPPORTED_ASSET_FORMAT","FORMAT_NOT_AVAILABLE","INVALID_FBX_ZIP_STRUCTURE","IMAGE_RESOLUTION_EXCEEDED","VALIDATION_FAILED","UPLOAD_EXPIRED","MESH_REQUIREMENTS_NOT_MET","COLLECTION_NOT_FOUND","PROJECT_NOT_FOUND","GRAPH_NOT_FOUND","NODE_NOT_FOUND","GROUP_NOT_FOUND","PROJECT_NODE_LIMIT_EXCEEDED","CONFLICT","ASSET_NOT_FOUND","ASSET_IN_USE","BATCH_TOO_LARGE","CONCURRENT_EXECUTION_LIMIT_EXCEEDED","PBR_FAILURE_CAP_EXCEEDED","EXECUTION_NOT_FOUND","EXECUTION_IN_PROGRESS","RATE_LIMITED","SERVICE_UNAVAILABLE","MAINTENANCE_MODE","INTERNAL_SERVER_ERROR","WORKFLOW_CREATION_FAILED","WORKFLOW_API_ERROR"]},"PlanFeatureRequiredError":{"type":"object","required":["error"],"description":"The error envelope for a missing subscription feature.","properties":{"error":{"type":"object","required":["code","message","details"],"description":"The missing-feature error body.","properties":{"code":{"type":"string","const":"PLAN_FEATURE_REQUIRED","description":"Stable error code for a missing subscription feature."},"message":{"type":"string","description":"Human-readable description of the error."},"details":{"$ref":"#/components/schemas/PlanFeatureRequiredErrorDetails"}}}}},"PlanFeatureRequiredErrorDetails":{"type":"object","required":["feature","upgrade_plan_codes"],"description":"The missing feature and the active plans that provide it.","properties":{"feature":{"$ref":"#/components/schemas/SubscriptionFeature"},"upgrade_plan_codes":{"type":"array","description":"Active plans available for purchase that provide the missing\nfeature. The array uses plan class and opaque code order.\n","items":{"$ref":"#/components/schemas/SubscriptionPlanCode"}}}},"SubscriptionFeature":{"type":"string","description":"Product capability available to the account.\n\nPossible enum values:\n- `commercial_license`: commercial-use rights.\n- `api_pbr`: PBR generation through the Public Platform API.\n- `api_asset_download`: asset downloads through the Public Platform API.\n- `custom_polycount`: custom polygon counts for 3D generation.\n- `image_edit`: image editing.\n- `multiview`: multiview generation.\n- `asset_download`: asset downloads in the Marso web app.\n- `api_access`: access to the Public Platform API.\n- `scene_generation`: scene generation and multi-asset runs in the Marso web app.\n- `application_apps`: the Sim Ready and Game Ready apps.\n","enum":["commercial_license","api_pbr","api_asset_download","custom_polycount","image_edit","multiview","asset_download","api_access","scene_generation","application_apps"]},"SubscriptionPlanCode":{"type":"string","pattern":"^plan_[A-Za-z0-9]{7,64}$","description":"Opaque and immutable code for a subscription plan. A plan display name\ncan change. The code starts with `plan_`, followed by 7 to 64 ASCII\nletters or digits.\n"}},"responses":{"Unauthorized":{"description":"The API key is missing, invalid, or revoked. Possible error codes:\n`API_KEY_MISSING`, `API_KEY_INVALID`, `API_KEY_REVOKED`, or `UNAUTHORIZED`.\n","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"PlanFeatureRequired":{"description":"The account does not have the plan feature required by this operation\n(`PLAN_FEATURE_REQUIRED`). The Free plan lacks `api_access`, so every\noperation refuses a Free account with this response. An account that\nlacks `api_pbr` or `api_asset_download` also gets it on the routes that\nneed those features.\n","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlanFeatureRequiredError"}}}},"ExecutionNotFound":{"description":"The execution does not exist, does not belong to your account, or was not\ncreated by a single submission (`EXECUTION_NOT_FOUND`). The items of a\nbatch are read with `GET /batch/{batch_id}`.\n","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"RateLimited":{"description":"Rate limit exceeded (`RATE_LIMITED`). The response includes a `Retry-After`\nheader with the number of seconds to wait before retrying, plus the\n`X-RateLimit-*` headers. Those headers are also present on successful (2xx)\nresponses so a client can pace itself.\n","headers":{"Retry-After":{"description":"Seconds to wait before retrying.","schema":{"type":"integer"}},"X-RateLimit-Limit":{"description":"Request burst capacity for the API key.","schema":{"type":"integer"}},"X-RateLimit-Remaining":{"description":"Requests remaining in the current burst window.","schema":{"type":"integer"}},"X-RateLimit-Reset":{"description":"Unix epoch seconds when the burst capacity is fully restored.\n","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"paths":{"/pbr/executions/{execution_id}":{"get":{"operationId":"getPBRExecution","tags":["pbr"],"summary":"Retrieve a single PBR execution.","description":"Returns the status of one execution submitted with\n`POST /pbr/execute/mesh` or `POST /pbr/execute/image` and, once it\nfinishes, its generated material maps with ready-to-use download URLs, or\na typed error if it failed. This is the endpoint you poll after a single\nsubmission.\n\nThe status moves from `QUEUED` straight to its terminal value. A single\nexecution does not report `RUNNING`, so poll for a terminal status rather\nthan waiting for a start signal.\n\nThis endpoint serves single submissions only. The items of a batch are\nread with `GET /batch/{batch_id}`.\n","parameters":[{"$ref":"#/components/parameters/ExecutionId"}],"responses":{"200":{"description":"The status and final result of the execution.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PBRExecutionResponse"}}}},"400":{"description":"The execution ID is malformed (`MALFORMED_UUID`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/PlanFeatureRequired"},"404":{"$ref":"#/components/responses/ExecutionNotFound"},"429":{"$ref":"#/components/responses/RateLimited"}}}}}}
```


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation by asking a question.

Perform an HTTP GET request on the following URL with the `ask` and `goal` query parameters:

```
GET https://docs.m-xr.com/marso-studio-api/api/pbr.md?ask=<question>&goal=<user_goal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is what the user is ultimately trying to achieve, the reason they need the answer. Sharing it helps GitBook give you a better, more relevant answer. A goal is most helpful when it describes the outcome the user wants rather than restating the question. For example, with `ask=how do I create an API token`, a goal like `automate deployments from our CI pipeline` lets GitBook tailor the answer to that use case.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
