---
name: oknytt3d-printing
version: 1.10.1
api_version: v1
description: Get a real 3D-printing price for your own model file from Oknytt3D, a print shop in Sweden that ships worldwide, and order it for your principal.
homepage: https://oknytt3d.se
metadata:
  category: manufacturing
  api_base_url: https://api.oknytt3d.se
  reference_url: https://api.oknytt3d.se/skill/v1/reference
---

# Oknytt3D: 3D printing on demand

We print physical parts from a model file you supply, priced by slicing your
actual file. This is the core of our machine-facing API: the rules and the
calls. Every error code, edge case and field detail is in the reference. Fetch
it only when you need it:

```
GET https://api.oknytt3d.se/skill/v1/reference
```

Error responses carry `docs`, a link to the reference section that explains
them.

## If you cannot call this API

Every step needs HTTP requests, including a raw file upload. If you cannot send
them, stop: never estimate a price or say an order was placed. Tell your
principal so, and give them both ways forward:

- Upload the model at https://oknytt3d.se for an instant price and checkout.
- Or ask an AI agent that can make API calls and upload files, such as Claude
  Code, Codex CLI, Hermes or OpenClaw, to read this document.

## Who is who

- **The agent**: the software calling this API with a key. It is never the
  customer.
- **The principal**: the person or business the agent quotes and orders for,
  and the customer of record. On the order calls, `buyer` takes only `email`
  (the principal's); names, phone and address go in `shippingAddress`.
- **We**: Oknytt3D, the seller.

In this document, **you** is the agent reading it.

## Start here: capabilities

```
GET https://api.oknytt3d.se/agent/v1/capabilities
```

No authentication needed, but send your key once you have one: `phasesEnabled`
then answers for **your key** and `phasesEnabledFor` says `"key"`. It holds
every live value: formats and build volume (`geometry`); materials, tiers,
quantities, `quantityLimits` and colours (`printing`); currencies, the
currency rule, minimum order, `priceValidityDays` and `paymentMethods`
(`money`); any `promotion`; what works today (`phasesEnabled`); and the
current `skill` version. **Values there beat anything in this document.**
Without a key, `phasesEnabled.ordering` is always `false`: `ordering.access`
says how a key gets in. Do not build against a phase that is off for your key.

### Staying current

Every agent API response carries `X-Oknytt3d-Skill-Version`. If it is ahead of
your copy, re-fetch this document. Patch: wording only. Minor: additions only,
existing calls keep working. Major: something can break an existing call. Old
behaviour is not kept alongside new.

## Rules

1. **Formats: `stl`, `step`, `stp`, and `obj` only.** 3MF is rejected on
   this path; extract its mesh as STL or OBJ and send that.
2. **There is no currency or country parameter.** Every quote carries every
   currency we charge in `prices`. The delivery country decides which binds:
   `money.currencyByCountry`, else `money.defaultCurrency`. Quote the principal
   that entry and never convert yourself.
3. **Quote amounts are decimal strings in the currency's own units**
   (`"91.39"` in SEK is 91.39 kr; never divide by 100). A quote's `total` is
   **VAT included, shipping excluded**. Order amounts (preview, commit,
   read-back) are JSON **numbers** in the same units.
4. **`options` are free, and so is a requote to one listed there.** A
   requote that slices something new costs a quote (`quoteSpent: true`).
5. **Any whole quantity up to `printing.quantityLimits.maxQuantity` can be
   ordered.** `printing.quantities` is only what we pre-slice for free. Above
   the limit, email us.
6. **When the principal does not say:** keep the quote's own `qualityTier`,
   and choose the cheapest entry in `shippingOptions`, telling the principal
   which one and its price. An option whose name does not say "Trackable" has no
   tracking number.
7. **Go by `canOrder` and `orderAccess` from `/me`, never by `scopes`.**
   `scopes` lists only what we granted by hand.
8. **Read `shippingOptions` and colours fresh from preview every time.** Never
   reuse an id, an amount or a colour from memory or an earlier order.
9. **Commit is idempotent.** After a timeout, send the identical request again:
   you get the original order back with `alreadyExisted: true`, never a second
   one. A changed buyer, address, option, colour or quote set is a new order.
10. **The agent is never the customer.** Hand the principal `payUrl` and
    `expiresAt`, or complete the payment yourself only with means the principal
    gave you. Payment methods come from `money.paymentMethods`, never from
    memory.
11. **Pace your polling.** Quotes: every 5 seconds, with `?view=progress`;
    most finish within 10 s, large files take minutes.
    Orders: every few minutes, not seconds. `POLL_RATE_LIMITED` means slow
    down.
12. **Never state a number we did not give you.** No computed totals, no
    guessed weights or delivery dates. If something is unclear, ask us.

## The flow

### 1. Get a key

```
POST https://api.oknytt3d.se/agent/v1/register
{ "label": "my-agent", "email": "you@example.com" }
```

Returns a key **once**; store it before anything else. Send it as
`Authorization: Bearer <key>` on every call below. We email a confirmation
link. The response gives `verification_required`, `daily_quote_budget` and
`daily_quote_budget_when_verified`; an unconfirmed key may be answered
`AGENT_EMAIL_UNVERIFIED`. Confirm it if you mean to order. The quote budget is
per email address, not per key. `GET /agent/v1/me` shows it and costs nothing.

### 2. Upload the model: this asks for a price

```
POST https://api.oknytt3d.se/agent/v1/quotes
Authorization: Bearer <key>
Content-Type: application/octet-stream
X-Filename: bracket.stl
Content-Length: 481232

<the raw bytes of the file>
```

The body is the file itself: not JSON, not base64, not multipart (curl:
`--data-binary @bracket.stl`). Returns `202` with a `quoteRef`. The first
price runs at Standard, in PLA unless you add `?material=PETG`.

### 3. Poll, then read the quote once

```
GET https://api.oknytt3d.se/agent/v1/quotes/{quoteRef}?view=progress
```

Returns `status` (`queued`, `analyzing`, `completed` or `failed`),
`allAdvertisedOptionsReady`, `pendingOptions`, `completedOptionCount` and
`totalOptionCount`. Stop on `failed` and read `error.code`. Once
`allAdvertisedOptionsReady` is `true` (or at `completed`, if the principal
wants only the current selection), make one final full GET:

```
GET https://api.oknytt3d.se/agent/v1/quotes/{quoteRef}
```

It carries `prices` (per currency: `total`, `vat`, `subtotalExVat`, and
`discount` when one applies), `options`, `risks`, `priceValidUntil` (the
last moment these prices can be ordered, counted from the upload; a requote
does not extend it) and `geometryExpiresAt`. Each entry in `options` has
`material`, `qualityTier`, `quantity`, `status`, `isCurrent` and, when
done, `prices` with `total` and `perUnit`. A Standard x1 quote also
pre-slices an L: the other tiers at x1 and the preset quantities at Standard.
Any other selection is sliced alone.

### 4. Requote: change material, tier or quantity

```
POST https://api.oknytt3d.se/agent/v1/quotes/{quoteRef}/requote
{ "material": "PETG", "quantity": 4 }
```

Omitted fields stay as they are. Poll again as in step 3. To compare a new
material, requote it at `quantity: 1`; ask for the exact quantity once the
principal picks. Colour is chosen when ordering.

### 5. May your key order?

`GET https://api.oknytt3d.se/agent/v1/me` answers `canOrder`. When it is `false`,
`orderAccess` says why: `needs_terms` (step 6), `needs_verification` (the
emailed link must be clicked), `not_offered` or `blocked` (email us).

### 6. Accept the ordering terms, once per key

`GET https://api.oknytt3d.se/agent/v1/me/order-terms` returns the text and its version.
Accept only if the terms hold for you:

```
POST https://api.oknytt3d.se/agent/v1/me/order-terms
{ "accept": true, "version": "2026-09-28.2" }
```

When we change the terms, every key accepts again before its next order.

### 7. Preview: free, creates nothing

```
POST https://api.oknytt3d.se/agent/v1/orders/preview
{
  "quoteRefs": ["qr_..."],
  "buyer": { "email": "buyer@example.com" },
  "shippingAddress": {
    "first_name": "Ada", "last_name": "Lovelace",
    "address_1": "Storgatan 1", "city": "Uppsala",
    "postal_code": "753 20", "country_code": "se",
    "phone": "+46 70 000 00 00"
  },
  "filaments": { "qr_...": { "color": "White" } }
}
```

Up to 20 of your finished quotes, each within its `priceValidUntil`, become
one order and one parcel. European destinations only. Returns `currency`
(upper-case, as in `prices`), `totals`, `lines`, `shippingOptions`,
`minimumOrderTopUp` and `priceValidUntil`.

- `subtotal` is the goods excluding VAT and `tax` is the VAT on the goods only.
  Shipping amounts already include their own VAT: never add VAT to shipping,
  and never report `tax` as the VAT on the whole order.
- No option is chosen yet, so the preview `total` excludes shipping. Each
  option's `orderTotal` is the whole order with it: quote that, never your
  own sum. The commit's `total` is exactly what the principal pays.
- `minimumOrderTopUp` (VAT included, `0` when none) is already inside
  `totals`, with no line of its own. Show it as it is.
- **Colour:** each line is printed in one colour, from its `filamentChoices`.
  A material on sale in several colours needs one per line, or commit refuses
  it with `COLOR_REQUIRED`: ask the principal.

### 8. Commit: create the pay link

```
POST https://api.oknytt3d.se/agent/v1/orders
{ ...the same body as preview..., "shippingOptionId": "so_..." }
```

Needs the full address: `first_name`, `last_name`, `address_1`, `city`,
`postal_code` and `country_code`. Returns `201` with `orderRef` (keep it),
`payUrl`, `expiresAt`, `status: "awaiting_payment"`, `currency`, `totals`,
`lines` and `shipping`. Every commit repeats what you accepted in the terms.

### 9. Hand over the link, or pay it

Each method in `money.paymentMethods` for the order's `currency` carries
`completion`: `agent_or_principal` means you may finish it with means the
principal gave you (a login or 3-D Secure step may still need them);
`principal` means hand the link over. The link works until `expiresAt`.
Whoever opens it may change the address, shipping or cart before paying.

### 10. Read back

```
GET https://api.oknytt3d.se/agent/v1/orders/{orderRef}
```

`status` is `awaiting_payment` (with `payUrl` again), `paid`, `expired`
(quote again) or `canceled`. A paid order carries `orderNumber`, the number on
the principal's confirmation, and `production.stage`: `received`, then
`in_production`, then `shipped`. The response never contains the principal's
name, address, email or phone.

## Tell the principal

- A quote is **before shipping**. Shipping is priced at checkout against the
  delivery address.
- Orders below our minimum (150.00 SEK or 15.00 EUR) are topped up at
  checkout, visibly. While a capacity promotion's discount applies, the
  promotional price is not topped up.
- A `discount` from a promotion is provisional until checkout confirms the
  promotional capacity.
- `seriesProduction` on a quote, or `largeOrder` on an order, is advice, not
  a refusal: the price stands, and emailing us may get a better one.
- Show every `risks[].message` on a completed quote. The price stands, but the
  principal should confirm it is what they meant (for example the units).
- Large quantities print across several plates (`plateCount`), so the per-piece
  price stops falling in a straight line there.
- We email the principal the confirmation, production and shipping updates,
  including tracking.

## When something goes wrong

Every error response has `code` and `message`, and `docs` links to the
reference section that explains it. A failed quote carries `error.code` (see
the reference's `failures` section): `SLICER_FAILED` is worth one retry;
retrying most others unchanged will not help. `410 QUOTE_EXPIRED` means the
quote is gone on our side: upload again. `ENDPOINT_NOT_AVAILABLE` means the
path does not exist: check `phasesEnabled`.

## Contact

A human reads [hello@oknytt3d.se](mailto:hello@oknytt3d.se). Write for
quantities above `maxQuantity`, series production, an ordering grant, doubts
about whether a part can be printed, or problems with this API. Label who wrote
it and who reads the reply, as the reference's `contact` section says.
