Submit a product for review

View as Markdown

Uploads a design file and creates a private product from it. 3DAPI then reviews the design, which a person does, so do not poll for it: wait for the product.approved or product.rejected webhook (or check review.status later), then run publish_product.

Variants, images and attachments can be sent in the same create_product call; this workflow keeps the minimum.

What you need

  • namestringRequired

    Product name.

  • file_namestringRequired

    Name of the design file, for example tracker.3mf.

  • content_typestringRequired

    MIME type of the design file: model/3mf, model/stl, model/obj or application/octet-stream.

  • size_bytesintegerRequired

    Size of the design file in bytes, up to 150 MB.

  • materialsarray of stringRequired

    Catalog material ids (mat_...) the design may be printed in. list_materials lists them.

  • idempotency_keystring

    A unique string for this attempt, for example a UUID. Send the same value when you retry, so the write happens once.

Step 1: Start a file upload

POST/v2/uploads

Reserve an upload for the design file. Then send the file bytes to upload_url with the returned method and headers. That transfer goes straight to storage, not to this API, so it is not a step here; do it before the next step.

Request body
{
  "purpose": "design_file",
  "file_name": "$inputs.file_name",
  "content_type": "$inputs.content_type",
  "size_bytes": "$inputs.size_bytes"
}

Succeeds when:

  • $statusCode == 201

Keeps for the next steps:

  • upload_id: $response.body#/id
  • upload_url: $response.body#/upload_url
  • method: $response.body#/method
  • headers: $response.body#/headers

Step 2: Create a product

POST/v2/products

Create the private product with the uploaded file as its design. It starts with review.status: pending. A full plan is refused with plan_limit_reached, whose details.fix_url opens the upgrade page; a file that never reached upload_url is refused with upload_missing.

Parameters
{
  "header Idempotency-Key": "$inputs.idempotency_key"
}
Request body
{
  "name": "$inputs.name",
  "designs": [
    {
      "key": "main",
      "file": {
        "upload": "$steps.upload_design.outputs.upload_id"
      },
      "materials": {
        "mode": "single",
        "allowed": "$inputs.materials"
      }
    }
  ]
}

Succeeds when:

  • $statusCode == 201

Otherwise:

  • retry_server_error: retry after 5 seconds, at most 3 times, when $statusCode >= 500.
  • plan_limit_reached: stop the workflow when $statusCode == 403.

Keeps for the next steps:

  • product_id: $response.body#/id
  • review_status: $response.body#/review/status

As a workflow file

The workflow returns:

  • product_id: $steps.create_product.outputs.product_id
  • review_status: $steps.create_product.outputs.review_status

Values starting with $ are filled in as you go: $inputs.x is something you provide, $steps.s.outputs.o is a value an earlier step returned, and $response.body#/path a field of this response.

This recipe is the workflow submit_product of arazzo.json, an Arazzo 1.0 document that tools and AI agents can follow step by step.