Printeers

Creating Orders

Learn how to create orders through the API, including image uploads, shipping options, and address handling.

Overview

Creating an order is a two-step process: upload print images, then place the order. The flow looks like this:

                Upload print image (JPEG/PNG)
                        POST /images
                             │
         Browse catalog      │
         GET /products       │
                 │           │
                 ▼           ▼
           Product_xxx + Image_xxx + quantity
                   POST /orders
                         │
                         ▼
                    Order_xxx
                         │
            ┌────────────┴────────────┐
            ▼                         ▼
       "dropship"                "combined"
   Ships to customer         Ships to your location

Images

Print products require a print image. Upload each image as a POST /images request with the raw image bytes in the body.

curl -X POST https://api.printeers.com/v2/images \
  -H "X-Printeers-Secret-Key: $KEY" \
  -H "Content-Type: application/octet-stream" \
  --data-binary @design.png
{
  "image": {
    "reference": "Image_pdcptbw3k48sv03wy338bb8jzm"
  }
}

Accepted formats are JPEG and PNG. Images larger than 7000x7000 pixels are automatically downsized. Uploading the same image twice returns the same reference. Image uploads count against the uploads rate-limit bucket; see the Rate Limiting guide.

Orderlines with non-print products (e.g. a screenprotector) must not have an image reference.

Shipping Kinds

Every order has a shipping kind that determines where the package is sent.

Kind Destination Use case
dropship Customer address you provide Direct-to-consumer fulfillment
combined Your warehouse You handle final distribution

Dropship orders ship directly to your customer. You provide the destination address and can optionally set a minimal shipping level and preferred packaging.

Combined orders are consolidated with other orders and shipped to your business address. No address or shipping options are needed; they use the combined address configured on your store.

Quoting Shipping Costs

Before placing a dropship order you can ask which shipping levels it can ship with, and what each costs, with POST /quote. Nothing is stored. The request takes only the fields of POST /orders that determine shipping, under the same names; leave out the other order fields, such as images, annotations and the custom identifier.

curl -X POST https://api.printeers.com/v2/quote \
  -H "X-Printeers-Secret-Key: $KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "quote": {
      "storeReference": "Store_9jmn3dewwvy13rv12k7aannqzy",
      "shippingKind": "dropship",
      "dropshipAddress": {
        "company": "",
        "firstname": "John",
        "lastname": "Doe",
        "streetname": "Koelmalaan",
        "housenumber": "330",
        "housenumberAddition": "",
        "additionalInfo": "",
        "state": "",
        "zipcode": "1432 PV",
        "city": "Aalsmeer",
        "countryCode": "NL",
        "phonenumber": "+31612345678",
        "email": "john@example.com"
      },
      "orderlines": [
        {
          "productReference": "Product_4j8k2m5n7p9q1r3s6t8v0w2x4y",
          "quantity": 1
        }
      ]
    }
  }'
{
  "shippingOptions": [
    { "level": "basic", "price": 395 },
    { "level": "tracked", "price": 695 },
    { "level": "express", "price": 1295 }
  ]
}

Prices are in EUR cents. The first option is the lowest level the order can ship with: the dropshipMinimalShippingLevel you asked for (default basic), raised or lowered where the destination, the weight or the value of the order requires it. The higher levels follow as alternatives, and only levels the order can actually ship with are listed: a Packstation, for example, is served by tracked only, so the level you asked for is not guaranteed. Pass the level your customer picks as dropshipMinimalShippingLevel when you create the order.

Combined orders ship to your business address together with other orders and have no shipping options of their own, so their quote has an empty shippingOptions list.

A quote validates the store, the products and the destination country the way order creation does, and returns the same error codes, for example store_not_writable_via_api, product_not_orderable or invalid_address_denied_country. The remaining address checks (phone number, email address, field lengths) happen when the order is created.

Creating an Order

Dropship

curl -X POST https://api.printeers.com/v2/orders \
  -H "X-Printeers-Secret-Key: $KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "order": {
      "storeReference": "Store_9jmn3dewwvy13rv12k7aannqzy",
      "shippingKind": "dropship",
      "customIdentifier": "my-order-123",
      "dropshipAddress": {
        "company": "",
        "firstname": "John",
        "lastname": "Doe",
        "streetname": "Koelmalaan",
        "housenumber": "330",
        "housenumberAddition": "",
        "additionalInfo": "",
        "state": "",
        "zipcode": "1432 PV",
        "city": "Aalsmeer",
        "countryCode": "NL",
        "phonenumber": "+31612345678",
        "email": "john@example.com"
      },
      "dropshipMinimalShippingLevel": "tracked",
      "orderlines": [
        {
          "productReference": "Product_4j8k2m5n7p9q1r3s6t8v0w2x4y",
          "imageReference": "Image_pdcptbw3k48sv03wy338bb8jzm",
          "quantity": 1
        }
      ]
    }
  }'
{
  "order": {
    "reference": "Order_5jcavdegg8n9pvgxq887jq0zbc"
  }
}

Combined

curl -X POST https://api.printeers.com/v2/orders \
  -H "X-Printeers-Secret-Key: $KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "order": {
      "storeReference": "Store_9jmn3dewwvy13rv12k7aannqzy",
      "shippingKind": "combined",
      "orderlines": [
        {
          "productReference": "Product_4j8k2m5n7p9q1r3s6t8v0w2x4y",
          "imageReference": "Image_pdcptbw3k48sv03wy338bb8jzm",
          "quantity": 5
        }
      ]
    }
  }'
{
  "order": {
    "reference": "Order_j2rznt7qr6ra78zb0aq2hsp3fd"
  }
}

When order creation fails, the response includes an error code and a human-readable message:

{
  "code": "missing_address",
  "message": "Dropship address is required for dropship orders"
}

Common errors include missing_address (dropship order without a destination address) and missing_image_reference (print product without an uploaded image). For the full list of request fields and error codes, see the Orders API Reference.

Annotations

Each orderline accepts up to 10 key-value annotations, for example to keep your own line identifiers or variant details with the order:

{
  "productReference": "Product_4j8k2m5n7p9q1r3s6t8v0w2x4y",
  "quantity": 1,
  "annotations": [
    { "key": "shop-line-id", "value": "4711" },
    { "key": "erp-article-number", "value": "ART-20481" }
  ]
}

Annotations are stored with the orderline and returned unchanged in the annotations array of each orderline in GET /orders/{orderReference}. The array is always present and empty for orderlines that were created without annotations.

Canceling Orderlines

As long as an order is not in production, you can cancel (part of) the quantity of its orderlines with POST /orders/{orderReference}/cancel.

The endpoint uses absolute totals: for every orderline you list, canceledQuantity is the desired total canceled quantity of that line, exactly as returned by GET /orders/{orderReference}. It is not the amount to cancel on top of the current value. Orderlines you do not list keep their current canceled quantity.

A typical flow is to fetch the order first and then submit the new totals:

curl https://api.printeers.com/v2/orders/Order_5jcavdegg8n9pvgxq887jq0zbc \
  -H "X-Printeers-Secret-Key: $KEY"

Response:

{
  "order": {
    …
    "orderlines": [
      { "index": 1, "quantity": 5, "canceledQuantity": 0, "shippedQuantity": 0, … },
      { "index": 2, "quantity": 2, "canceledQuantity": 0, "shippedQuantity": 0, … }
    ]
  }
}

To cancel 3 pieces of the first line and the complete second line:

curl -X POST https://api.printeers.com/v2/orders/Order_5jcavdegg8n9pvgxq887jq0zbc/cancel \
  -H "X-Printeers-Secret-Key: $KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "orderlines": [
      { "index": 1, "canceledQuantity": 3 },
      { "index": 2, "canceledQuantity": 2 }
    ]
  }'

A successful request returns 204 No Content. Because the request describes the desired end state, sending the same request again also returns 204 without canceling anything further. This makes retries after a timeout safe.

The request is atomic: when any listed orderline fails validation, nothing is canceled.

Canceling cannot be undone. If you send a canceledQuantity below the current canceled quantity of a line, the request is rejected with 409 canceled_quantity_below_current. This usually means the order changed since you last fetched it, for example because our support team canceled quantity on your behalf. Fetch the order again to see the current state.

Other things to know:

  • Once a quantity has been shipped it can no longer be canceled. The canceled quantity is limited to quantity - shippedQuantity per line (409 canceled_quantity_exceeds_shippable).
  • Orders that are in production cannot be canceled (409 already_in_production).
  • Orders in stores backed by an upstream platform such as Shopify or WooCommerce must be canceled in that platform instead (403 store_not_writable_via_api).

For the full list of error codes, see the Orders API Reference.

Changing the Dropship Address

As long as a dropship order is open, you can replace its destination address with PUT /orders/{orderReference}/dropship-address. An order is open until every orderline has been shipped or canceled.

The request carries a complete address, validated with the same rules as POST /orders. It replaces the previous address entirely: there is no partial update, so send every field, including the ones that stay the same.

curl -X PUT https://api.printeers.com/v2/orders/Order_5jcavdegg8n9pvgxq887jq0zbc/dropship-address \
  -H "X-Printeers-Secret-Key: $KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "dropshipAddress": {
      "company": "",
      "firstname": "Jane",
      "lastname": "Doe",
      "streetname": "Laan van Alkmaar",
      "housenumber": "12",
      "housenumberAddition": "A",
      "additionalInfo": "",
      "state": "",
      "zipcode": "1811 AA",
      "city": "Alkmaar",
      "countryCode": "NL",
      "phonenumber": "+31612345678",
      "email": "jane@domain.example"
    }
  }'

A successful request returns 204 No Content. The order now carries a new dropshipAddressReference, which GET /orders/{orderReference} returns. The previous address is no longer linked to the order, so its reference stops resolving through GET /addresses/{addressReference} unless a shipment of the order was already created with it. Fetch the order again to pick up the new reference.

Other things to know:

  • A shipment that was already created keeps the address it was created with. Change the address before the order ships; once every orderline has shipped or been canceled the request is rejected (409 order_completed).
  • Combined orders have no destination address (409 order_not_dropship).
  • Replacement orders cannot change their address (409 replacement_address_locked): they are sent to the destination of the claim they settle.
  • Address validation errors use the same 400 codes as order creation, for example invalid_address_missing_email or invalid_address_denied_country.

For the full list of error codes, see the Orders API Reference.

Typical Integration

A typical order creation flow follows these steps:

  1. Browse the catalog: call GET /products to find available products and their references.
  2. Upload images: send each print image to POST /images and store the returned references.
  3. Create the order: call POST /orders with the product references, image references, quantities, and shipping details.
  4. Track the order: use GET /orders/{orderReference} to check order status and retrieve shipment references.
  5. Track shipments: once shipped, use GET /shipments/{shipmentReference} for tracking information. See the Shipments and Tracking guide.