Send us your own 3D files from your own system and we print them: an instant price for each file, an order with one call, and status updates to your server as the job moves through our studio. The prices are the ones our website quotes for the same file and choices.
1. How it works
- Get a key. Under Webhooks and keys on this page, make a test key to build against (orders never print, nothing is charged) and a live key when you are ready.
- Quote.
POST /quoteswith links to your files and your choices (process, material, colour, quantity). We fetch each file, measure it ourselves and price it. A quote holds for 7 days. - Order.
POST /orderswith the quote id, your reference, and where the parts go. The price and the exact file are locked to the quote. - Pay. Your account is prepaid: an order starts as soon as your balance covers it. Pay in under Account (PayNow or card), or turn on auto top-up with a saved card.
- Follow it. We send signed events to your webhook as the order moves: accepted, printing, ready, shipped, completed.
2. Base URL and keys
https://www.additiveinn.com/api/partner/v1
Authorization: Bearer ai_live_… (or ai_test_… for the sandbox)
Content-Type: application/json
A key is shown once when you make it; we keep only its hash. Revoke a key on this page at any time. Test keys see only sandbox quotes and orders, and live keys only live ones.
3. Print options
GET /print-options lists what you can choose: each technology (for
example FDM), its materials with their colours and allowed finishing
ids, its layerHeightsMm, its buildVolumeMm, the leadTimes
(standard, express), minOrderValueCents and the file formats
(stl, 3mf, obj). Options change as our studio adds materials; read
them rather than hard-coding them.
4. Quotes
POST /quotes
{
"leadTime": "standard",
"lines": [
{
"lineRef": "BRACKET-1",
"filename": "bracket.stl",
"url": "https://files.example.com/bracket.stl",
"sha256": "9f2c…64 hex…",
"bytes": 482113,
"options": {
"technology": "FDM",
"material": "pla",
"colour": "Black",
"quantity": 20,
"infillPct": 15,
"walls": 2,
"layerHeightMm": 0.2,
"finishing": ["pp_1"]
}
}
]
}
- Up to 20 lines.
urlis public https; we fetch it once and refuse it if itssha256orbytesdiffer from what you declared. - Every option is optional; anything left out takes our website's default.
- STL, 3MF (one build plate per file) and OBJ, up to 50 MB. Export STEP or IGES as STL first. STL and OBJ are read in millimetres.
- A quote has 55 seconds for all its files, fetching and measuring
(
504 quote_timeoutpast that: send fewer or smaller files per quote), and you can have two quotes running at once (429 quotes_busyfor a third).
The answer (201) prices each line from the file itself: geometry
(volumeCm3, areaCm2, sizeMm), the resolved options, unitCents and
lineCents, then minOrderTopUpCents (an order below the minimum is topped
up to it), subtotalCents, gstCents and totalCents. All amounts are
Singapore dollars in cents; subtotalCents is before GST.
A line is refused (422, with the line's path and a code) when the file
cannot be read, a choice is not on offer, the part does not fit our printers
in any orientation, or the line is large enough to need a person's quote
(needs_review): ask us then, and we quote it directly.
GET /quotes/{quoteId} reads a quote back. Its status is open,
ordered or expired.
5. Orders
POST /orders with an Idempotency-Key header (your reference plus a
revision, for example BN-100-r1; a retry with the same key and body
returns the same order).
{
"quoteId": "QP-…",
"partnerOrderRef": "BN-100",
"customer": { "businessName": "Your customer or you" },
"fulfilment": { "mode": "partner_collect", "priority": "standard" },
"notes": "Optional, up to 2000 characters"
}
priorityisstandard, orrushwhen the quote'sleadTimewasexpress; it must match the quote.fulfilment.mode:partner_collect(you collect from our studio),partner_courier_pickup(your courier collects), oradditive_inn_shipwithshipTo { name, line1, line2?, postcode, phone? }in Singapore; we add a courier estimate and invoice the courier's actual fare plus S$2.- An order names a quote and nothing that costs money. A quote orders once
(
409 quote_used) and not after it expires (409 quote_expired).
An order the balance does not yet cover is accepted with
billing.hold.reason = "prepay_required" and a payUrl; it starts the
moment it is paid for. GET /orders/{orderId}, GET /orders?since= and
POST /orders/{orderId}/cancel work as for every partner: free before
printing starts.
GET /account shows your balance (availableCents) and what is on hold.
6. Webhooks
PUT /webhook with { "url": "https://…" } (one URL per environment), and
POST /webhook/ping to test it. Every event is a POST signed with your
signing secret, shown under Webhooks and keys:
X-AI-Event-Id: evt_…
X-AI-Signature: t=1758261911,v1=<hex HMAC-SHA256 of "<t>.<raw body>" with your signing secret>
Check the signature and answer 2xx; we retry failures for about 15 hours.
Events carry the order as GET /orders/{orderId} shows it: order.received,
order.accepted, order.needs_info, order.status_changed (every move),
order.shipped, order.completed, order.cancelled, and the billing
events (order.billing_hold, order.billing_released). The same event id
can arrive twice; act on it once.
7. Errors
Errors are JSON: { "error": { "code", "message", "details"? } }.
401 a missing or unknown key, 404 not found (or another environment's),
409 a conflict (quote_used, quote_expired, idempotency_conflict),
413 a file over the limit, 422 a problem with what you sent (each in
details with a path), 429 too many requests (60 a minute, 20 quotes an
hour, two quotes at once; wait retryAfter seconds), 504 quote_timeout a
quote that ran out of time.