Documentation
Partner API

Orders

Create an order with your reference, items, your customer's address and white-label instructions; pricing, payment, acceptance, holds and cancelling.

Updated 2026-10-24MarkdownOpenAPI

An order is one delivery to one of your customers: one or more items — each a ready file in a material at a quantity — plus your own reference, the delivery address, white-label instructions and any metadata you want back. You create it with one call; we price it, review it, accept it, produce it, ship it and tell you about every step.

Create an order

curl -X POST "https://engine.layercrew.com/v1/orders" \
  -H "Authorization: Bearer $LAYERCREW_API_KEY" \
  -H "Idempotency-Key: 2f5c1c2e-0f6a-4d2b-9a1e-7b3c4d5e6f70" \
  -H "Content-Type: application/json" \
  -d '{
  "reference": "SHOP-10231",
  "items": [
    {
      "file_id": "4f1b2c3d-5e6f-4a7b-8c9d-0e1f2a3b4c5d",
      "quantity": 2,
      "material": "PLA",
      "colour": "black",
      "nozzle": "0.4",
      "notes": "Visible face is the flat side."
    }
  ],
  "delivery": {
    "name": "Claire Martin",
    "company": "Atelier Martin",
    "phone": "+33 6 12 34 56 78",
    "email": "claire@example.com",
    "line1": "12 rue de la Paix",
    "line2": "Bâtiment B",
    "city": "Lyon",
    "postal_code": "69001",
    "country_code": "FR"
  },
  "white_label": {
    "sender_name": "Studio NS",
    "card_text": "Merci Claire — Studio NS",
    "packaging_preset": "neutral",
    "no_invoice_in_box": true,
    "language": "fr"
  },
  "notes": "Fit check before the batch, please.",
  "metadata": {
    "shop_order_id": "10231",
    "customer_id": "c_8812"
  }
}'

The answer is 202 with the order in received and pricing_status: pending. Within minutes every item is priced and order.priced follows with the order in pending_review. The Idempotency-Key header is required: see Idempotency.

The body

FieldRequiredRules
referenceyesYour own identifier, up to 120 characters, unique among your orders in this environment.
items[]yesOne to your plan's max_items_per_order (max_test_items_per_order in test).
items[].file_idyesA ready file of yours in the same environment.
items[].quantityyes1 to your plan's max_quantity_per_item.
items[].materialyesA material from GET /v1/materials.
items[].colournoA colour id of that material. Left out, the engineer picks at production.
items[].nozzlenoFrom the material's nozzles; default 0.4.
items[].notesnoUp to 500 characters, read by the engineer.
deliveryyesYour customer's address — below.
white_labelnoSender name, card text, packaging — below. White label is on even when the block is left out.
notesnoUp to 2000 characters, read by the engineer.
metadatanoUp to 20 keys with string values up to 200 characters; returned untouched everywhere.

Reference

reference is your identifier for the order — a shop order number, a job id. It must be unique among your orders in the environment: a duplicate answers 409 REFERENCE_TAKEN and error.details.order_id names the existing order, so even a retry without its Idempotency-Key finds its order. Look orders up by it with GET /v1/orders?reference=SHOP-10231.

Items

  • file_id must be a ready file of yours in the same environment — 409 FILE_NOT_READY otherwise.
  • material, and the optional colour id, come from GET /v1/materials and your plan — 422 MATERIAL_UNAVAILABLE otherwise.
  • A 3MF with several objects is priced as the plate it holds; estimate.unit_yield says how many parts one plate makes.
  • Every string passes a cleaning step that drops control characters and markup before it is stored.

Delivery

Your customer's address as the courier needs it: name, phone (the courier requires it), line1, city, postal_code and country_code (ISO 3166-1 alpha-2) are required; company, email, line2 and region are optional outside Romania.

Delivery fields are personal data of your customer. We process them under the data processing agreement, show them to production and shipping staff only, redact them after your plan's recipient_retention_days (90 days on the default plan) and at once on request: DELETE /v1/orders/{id}/recipient on a finished order keeps city, postal code and country for fiscal records and replaces the rest with [redacted], on the order and on its stored events. order.recipient_redacted confirms it.

White label

White label is on by default. Every order ships in neutral packaging with no LayerCrew marks on the parts, the packaging or the inserts, and no invoice in the box.

FieldDefaultMeaning
enabledtrueSet false only if you want our standard packaging.
sender_nameyour company nameThe contact line on the courier label, up to 80 characters.
card_textnonePrinted on a card in the parcel, up to 500 characters; can differ on every order.
packaging_presetneutralneutral, or partner_supplied when you send us your packaging.
no_invoice_in_boxtrueThe invoice goes to you, never into the parcel.
languageyour account's localeThe language of any printed insert, as a two-letter code.

Metadata and notes

metadata is yours: up to 20 keys with string values up to 200 characters, returned untouched on the order and in every webhook — a shop order id, a customer id, a campaign. notes (up to 2000 characters) and items[].notes are read by the engineer who reviews the order.

Pricing

pricing carries currency (EUR), subtotal, discount_total, shipping_total, tax_total, tax_treatment and grand_total, each a string with two decimals. Per item: list_unit_price, discount_pct, unit_price, line_total and the slicer's estimate. The block is provisional at order.priced (final: false) and frozen at acceptance (final: true); once frozen it is the price you pay.

tax_treatmentWhen
standardRomanian VAT applies — a Romanian company, or a company without a valid EU VAT number.
reverse_chargeAn EU company with a valid VAT number: no VAT on the invoice, you account for it.
exportDelivery outside the EU.
exemptAn exemption agreed in writing.
testA test order; nothing is invoiced.
JSON
{
  "pricing": {
    "currency": "EUR",
    "subtotal": "24.80",
    "discount_total": "0.00",
    "shipping_total": "14.99",
    "tax_total": "0.00",
    "tax_treatment": "reverse_charge",
    "grand_total": "39.79",
    "final": true
  },
  "payment": {
    "method": "bank_transfer",
    "status": "unpaid",
    "ship_before_payment": false,
    "amount_due": "39.79",
    "instructions": {
      "beneficiary": "LAYER CREW SRL",
      "iban": "RO49AAAA1B31007593840000",
      "bank": "Example Bank",
      "reference": "PA-2026-0007"
    },
    "paid_at": null
  }
}

pricing and payment on an accepted order

Payment

Payment is by bank transfer against the order. payment.instructions appear at acceptance — beneficiary, iban, bank and the reference to put on the transfer, which is the order number. On the default plan production starts once the transfer arrives and order.paid is emitted; ship_before_payment: true on a plan or an agreed override starts production at acceptance instead. The invoice follows as order.invoiced: GET /v1/orders/{id}/invoices lists it and GET /v1/orders/{id}/invoices/{invoice_id}/download-url returns a 15-minute link to the PDF. Card payment is a later addition; a plan with payment terms is agreed in writing.

Acceptance and rejection

Every order is read by an engineer before it is accepted, usually within one business day — the same design-for-manufacturing look every file gets. An accepted order carries accepted_at, a frozen pricing block and payment.instructions. A declined order is rejected with rejection.code, rejection.message (the fact and the next step) and rejection.note (what the engineer wrote for you).

rejection.codeWhat it meansNext step
needs_splitThe part is laid out larger than our print bed.Split it into sub-assemblies, or ask us to take it by hand.
design_change_neededA feature will not print as drawn; the note says which.Change the design and order again.
material_unavailableThe material is out of stock for this run.Pick another material, or wait for stock.
out_of_capacityThe queue is full for the date you need.Order again with a later date in mind.
address_unserviceableThe courier has no route to the address as given.Correct the address and order again.
price_reviewThe order needs a price by hand.We write to you with the number.
otherSee the note.The note says what to do.

Holds

When we need something from you — a question about a feature, a colour that ran out — the order goes on_hold with hold.reason and hold.since and order.on_hold is emitted. order.released brings it back to the state it had before. Holds are possible from received through ready_to_ship.

Cancelling

POST /v1/orders/{id}/cancel with an optional reason is allowed while the order is received, pending_review or accepted — before production starts. Once printing has started the call answers 409 CANNOT_CANCEL; write to us from the order page in your dashboard and we tell you what is still possible. A cancelled order is final and is never invoiced.

Reading orders

  • GET /v1/orders/{id} takes the id or the number (PA-2026-0007).
  • GET /v1/orders?status=&reference=&created_after=&page=&per_page= lists newest first, 25 a page and up to 100.
  • GET /v1/orders/{id}/events returns the order's events in seq order; GET /v1/events the whole account's. See Lifecycle and events.
  • links.self and links.events on every order point at both.

Partner orders are yours alone: they are invisible on the customer-facing routes and on the customer dashboard, and visible under Integrations → Orders in yours. Every endpoint is in the reference.

We value your privacy

We use cookies for analytics and advertising to improve your experience and measure our marketing. See our Privacy Policy.