These docs describe the reference manufacturing API at sinter.systems. A sinter.ac subscription covers the project-planning workspace; manufacturing API access is separate.
API reference
- Base URL
https://sinter.systems- Format
- JSON; POST /v1/quotes is multipart/form-data.
- Errors
- Statuses and errors
- Schema
- /docs/openapi.json
GET /v1/mePOST /v1/billing/portalPOST /v1/device/startPOST /v1/device/tokenGET /v1/device/{user_code}POST /v1/device/{user_code}/approvePOST /v1/device/{user_code}/denyGET /v1/oauth/requests/{handle}POST /v1/oauth/requests/{handle}/approvePOST /v1/oauth/requests/{handle}/denyGET /v1/oauth/grantsDELETE /v1/oauth/grants/{grant_id}GET /v1/keysPOST /v1/keysDELETE /v1/keys/{key_id}GET /v1/materialsPOST /v1/quotesGET /v1/quotesGET /v1/quotes/{quote_id}POST /v1/quotes/{quote_id}/requotePOST /v1/quotes/{quote_id}/claimPOST /v1/quotes/{quote_id}/requestPOST /v1/quotes/{quote_id}/cancelPOST /v1/quotes/{quote_id}/deliveryPOST /v1/quotes/{quote_id}/acceptGET /v1/ordersGET /v1/orders/{order_id}POST /v1/orders/{order_id}/cancel
Account
GET/v1/me
API keyThe customer account and the key in use.
Returns · 200
customer | The account. |
key | name, prefix, scopes of the key in use. |
auth | How the call was authenticated. |
Errors
401 | Missing, wrong or revoked key. |
POST/v1/billing/portal
Signed-in person onlyA link to the card payment portal: saved cards and receipts.
- API keys get 403.
Returns · 200
url | The portal. |
Errors
404 | No billing account: the account hasn’t paid by card yet, or the portal isn’t set up. |
Device login
POST/v1/device/start
PublicStarts a device login.
Body · application/json · optional
| Name | Type | Notes |
|---|---|---|
name | string | What the agent or script is; shown to the person approving. Default "Agent". ≤ 80 chars. |
scopes | array of quotes | orders | purchase | quotes, orders, purchase. Default quotes and orders. purchase only if the person approving allows purchases. ≥ 1 item. |
Returns · 200
device_code | Secret. Used to poll for the key. |
user_code | The code the person approves. |
verification_url | Where the person approves it. |
verification_url_complete | The same page with the code filled in. |
expires_in | Seconds until the codes expire (600). |
interval | Seconds between polls. |
Errors
429 | Too many logins started. |
POST/v1/device/token
PublicReturns the API key once the person has approved the device login.
Body · application/json
| Name | Type | Notes |
|---|---|---|
device_code required | string | ≤ 200 chars. |
Returns · 200
api_key | The key. Shown once. |
key | name, prefix, scopes. scopes are what the person granted, which may be fewer than asked for. |
Errors
400 authorization_pending | Not approved yet. Poll again after interval seconds. |
400 slow_down | Polling too fast. Wait longer between polls. |
400 access_denied | Denied. Final. |
400 expired_token | Expired. Final. |
400 invalid_grant | Unknown or already used code. Final. |
429 | Too many polls. |
GET/v1/device/{user_code}
Signed-in person onlyA pending device login, for the /device page.
- API keys get 403.
Path
| Name | Type | Notes |
|---|---|---|
user_code required | string |
Returns · 200
scopes_supported | quotes, orders, purchase. |
POST/v1/device/{user_code}/approve
Signed-in person onlyApproves a device login.
- API keys get 403.
Path
| Name | Type | Notes |
|---|---|---|
user_code required | string |
Body · application/json · optional
| Name | Type | Notes |
|---|---|---|
scopes | array of quotes | orders | purchase | A subset of the scopes asked for. Left out: all of them. ≥ 1 item. |
Returns · 200
Errors
409 | Already approved or denied. |
POST/v1/device/{user_code}/deny
Signed-in person onlyDenies a device login.
- API keys get 403.
Path
| Name | Type | Notes |
|---|---|---|
user_code required | string |
Returns · 200
Errors
409 | Already approved or denied. |
Connected apps
GET/v1/oauth/requests/{handle}
Signed-in person onlyA pending MCP connection request, for the /oauth/consent page.
- API keys get 403.
- The first account to open a request holds it.
Path
| Name | Type | Notes |
|---|---|---|
handle required | string |
Returns · 200
client | id, name, claimed_uri (unverified), verified_domain (only for a client named by its metadata document URL). |
redirect_uri | Where the browser returns after the decision. |
requested_scopes | What the client asked for. |
scopes | Requested; once approved, granted. |
status | pending, approved or denied. |
expires_at | Unix time. 10 minutes after the request. |
Errors
403 | Opened by another account. |
404 | Unknown or expired. |
POST/v1/oauth/requests/{handle}/approve
Signed-in person onlyApproves an MCP connection request.
- API keys get 403.
Path
| Name | Type | Notes |
|---|---|---|
handle required | string |
Body · application/json · optional
| Name | Type | Notes |
|---|---|---|
scopes | array of quotes | orders | purchase | A subset of the scopes asked for. Left out: the asked-for scopes other than purchase. ≥ 1 item. |
Returns · 200
redirect_to | The client’s redirect URI with the code. |
Errors
400 | Scope not asked for, or none approved. |
403 | Opened by another account. |
404 | Unknown or expired. |
409 | Already approved or denied. |
POST/v1/oauth/requests/{handle}/deny
Signed-in person onlyDenies an MCP connection request.
- API keys get 403.
Path
| Name | Type | Notes |
|---|---|---|
handle required | string |
Returns · 200
redirect_to | The client’s redirect URI with error=access_denied. |
Errors
403 | Opened by another account. |
404 | Unknown or expired. |
409 | Already approved or denied. |
GET/v1/oauth/grants
Signed-in person onlyConnected apps: the account’s MCP connections.
- API keys get 403.
Returns · 200
grants[] | id, client_id, client_name, scopes, created_at, last_used_at, revoked_at. |
DELETE/v1/oauth/grants/{grant_id}
Signed-in person onlyRevokes an MCP connection and its tokens.
- API keys get 403.
Path
| Name | Type | Notes |
|---|---|---|
grant_id required | string |
Returns · 200
Errors
404 | Unknown grant, or another account’s. |
API keys
GET/v1/keys
Signed-in person onlyThe account’s API keys.
- API keys get 403.
Returns · 200
keys[] | name, prefix, scopes. Never the secret. |
scopes_supported | quotes, orders, purchase. |
POST/v1/keys
Signed-in person onlyCreates an API key. The secret is in this response only.
- API keys get 403.
Body · application/json · optional
| Name | Type | Notes |
|---|---|---|
name | string | Default "API key". ≤ 80 chars. |
scopes | array of quotes | orders | purchase | quotes, orders, purchase. Default quotes and orders. purchase is Allow purchases. ≥ 1 item. |
Returns · 201
DELETE/v1/keys/{key_id}
Signed-in person onlyRevokes an API key.
- API keys get 403.
Path
| Name | Type | Notes |
|---|---|---|
key_id required | string |
Returns · 200
Materials
GET/v1/materials
PublicMaterials, colors, print settings and lead times.
Returns · 200
materials[].id | PLA+, PLA-CF, PETG, PETG-CF, ASA, ASA-CF, PC, PC-CF, TPU, TPU-CF, Nylon, Nylon-CF, PPA-CF, PPS-CF, Custom. |
materials[].available | false: out of stock; new quotes in it are refused. |
materials[].lead_days | The material’s lead days in the default color (the first of colors), already included in a quote’s ship_by. PLA+ and PETG in black or white ship without material lead time; other materials and colours add one day. |
materials[].same_day_colors | The colors the material ships in without lead days; empty when every color adds one. A color change re-dates a quote. |
materials[].custom | true: any other material, named in material_note. |
speeds[] | speed, label, days and optional asap_offset_days of each lead time. Economy uses speed week, days null and asap_offset_days 7. |
colors | Filament colors; the first is the default. |
custom_color | The color to send with color_note for any other color (Custom); Sinter reviews it. null when not offered. |
print_settings[] | Settings a quote may change: name, label, type (float, int, choice or face), min and max or choices, unit; automatic: true when Sinter chooses it for the part if left out; range_by and ranges when the range follows another setting. Left out, a setting is Standard (or Automatic). The ranges are the printers’ own; see Materials. |
Quotes
POST/v1/quotes
Scope: quotesUploads a model and starts a quote.
- Returns status received, or quoted at once. Until quoted, needs_review, engineering or failed, poll GET /v1/quotes/{quote_id} every 5 to 10 seconds.
- Quoted prices are binding.
Headers
| Name | Type | Notes |
|---|---|---|
Idempotency-Key | string | Any unique string up to 200 characters. A retried upload returns the same quote. |
Body · multipart/form-data
| Name | Type | Notes |
|---|---|---|
file required | file | STEP, STL, OBJ or 3MF, up to 50 MB. |
material | PLA+ | PLA-CF | PETG | PETG-CF | ASA | ASA-CF | PC | PC-CF | TPU | TPU-CF | Nylon | Nylon-CF | PPA-CF | PPS-CF | Custom | Default "PLA+". |
material_note | string | Required for Custom: the material wanted. ≤ 320 chars. |
quantity | integer | Default 1. 1–100. |
units | mm | cm | in | What an STL or OBJ was drawn in. Ignored for STEP and 3MF. Default "mm". |
color | string | One of the colors in /v1/materials. ≤ 40 chars. |
color_note | string | Required for the Custom color: the color wanted. Sinter reviews the quote. ≤ 320 chars. |
Returns · 202
Errors
400 | Refused, for example a material out of stock. See detail. |
413 | File too large. |
415 | Unsupported file type. |
422 | Invalid input. |
429 | A limit was reached. Wait Retry-After seconds. |
GET/v1/quotes
Scope: quotesThe account’s quotes, newest first.
- limit: 1–100 (default 50). Pass next_cursor as cursor to read the next page; null means the last page.
Path
| Name | Type | Notes |
|---|---|---|
limit | integer | 1–100. |
cursor | string | ≤ 64 chars. |
Returns · 200
GET/v1/quotes/{quote_id}
Scope: quotesOne quote.
Path
| Name | Type | Notes |
|---|---|---|
quote_id required | string |
Returns · 200
status | See Statuses and errors. |
options[] | One per lead time: speed, label, days, ship_by, total_cents, unit_cents, available, note. days is null for current options: use ship_by as the Pacific calendar day it falls on. Economy has asap_offset_days: 7, relative to the Default ship date; material lead time is already included. |
tier | instant, assisted or engineered. |
file_id | The file the quote was made from: the same for every quote of the same file (its requotes, or the file uploaded again under any name), different for another file even under the same name. Not comparable across accounts. |
part | dimensions_mm: the part’s size as uploaded, in its own axes. |
ship_date_pending | true until Sinter confirms the ship date of an assisted quote. ship_by is null until then. |
estimate_cents | Status engineering only: an estimate. options is empty. |
estimate_pending | Status engineering only: true while estimate_cents updates after a change. |
request_status | Engineering review requests: draft (not sent; editable with requote), open (sent; editable with requote), accepted or rejected. null otherwise. |
units | mm, cm or in for STL and OBJ; null for STEP and 3MF. |
print_settings | Changes from Standard, by name. {} for none. |
manufacturability | Structured analysis: analysis_id, version, status, checked, not_assessed, review_required, review_completed and findings. Read again after changes; screened is not a full DFM certification. |
flags | Manufacturability findings: code, level (block or warning), message and features. Warnings can appear on quoted parts. Read alongside status and tier; an empty array is not a guarantee of suitability. |
progress | While received or processing: stage (queued, checking, processing), fraction (0 to 1), ahead (quotes queued before this one). |
review_url | The quote page, with a 3D preview. Anyone with the link can see it; only the account that owns the quote, signed in, can change, pay for or cancel it there. |
delivery | method (courier or shipping), city, fee_cents and, for shipping, transit_days, once delivery is priced. |
delivery_required | true: accept needs a delivery price. |
Errors
404 | Not found, or another account’s quote. |
POST/v1/quotes/{quote_id}/requote
Scope: quotesChanges material, quantity, units, color or print settings before payment.
- Status received or processing: changed in place, same id. A quantity, color or Custom material name change keeps the work under way; a material, units or print_settings change starts it again.
- Status engineering with request_status draft or open: changed in place, same id, still an engineering request. A material or print_settings change updates estimate_cents; estimate_pending is true until then. A change that no longer needs review, or a units change, returns a new quote and replaces this one.
- request_status accepted or rejected: a new request with its own id. The accepted quote stays payable as issued.
- Any other status: a new quote with its own id. An issued engineered quote stays payable as issued.
- Use the id returned. Returns status quoted, received or engineering; poll.
- Also refreshes an expired quote. There is no daily quote-count limit.
Path
| Name | Type | Notes |
|---|---|---|
quote_id required | string |
Body · application/json
| Name | Type | Notes |
|---|---|---|
material | PLA+ | PLA-CF | PETG | PETG-CF | ASA | ASA-CF | PC | PC-CF | TPU | TPU-CF | Nylon | Nylon-CF | PPA-CF | PPS-CF | Custom | |
material_note | string | Custom only. Kept when left out. ≤ 320 chars. |
quantity | integer | 1–100. |
units | mm | cm | in | Unchanged when left out. |
color | string | Unchanged when left out. ≤ 40 chars. |
color_note | string | Custom color only. Kept when left out. ≤ 320 chars. |
print_settings | object | Settings by name from /v1/materials print_settings, within their ranges. {} resets them to Standard (or Automatic). Unchanged when left out. |
Returns · 201
Errors
400 | Refused, for example a setting out of range. See detail. |
409 | Paid, or the file is no longer stored. |
429 | A limit was reached. Wait Retry-After seconds. |
POST/v1/quotes/{quote_id}/claim
Scope: quotesMoves a quote made without an account to this account.
- The quote gets a new review_url. The old link stops working.
Path
| Name | Type | Notes |
|---|---|---|
quote_id required | string |
Body · application/json
| Name | Type | Notes |
|---|---|---|
token required | string | The last part of the guest quote’s review_url. ≤ 200 chars. |
Returns · 200
Errors
404 | Token doesn’t match. |
409 | Another account has the quote, or a card payment is in progress. |
POST/v1/quotes/{quote_id}/request
Scope: quotesSends a draft engineering request (request_status draft) to Sinter’s engineers.
- Nothing is reviewed until it is sent. Send only when the user asks; let them change it with requote first.
- Returns the quote with request_status open. Sending one already sent returns it unchanged.
- An unsent draft expires a day after pricing. A sent request expires 14 days after sending unless issued.
Path
| Name | Type | Notes |
|---|---|---|
quote_id required | string |
Returns · 200
Errors
409 | Not status engineering, or the draft expired. Requote it. |
POST/v1/quotes/{quote_id}/cancel
Scope: quotesWithdraws a quote in status engineering.
Path
| Name | Type | Notes |
|---|---|---|
quote_id required | string |
Returns · 200
Errors
409 | Any other status. Not withdrawn. |
POST/v1/quotes/{quote_id}/delivery
Scope: quotesPrices delivery to an address: local courier, or carrier shipping if the user chooses it.
- Local courier inside the San Francisco Bay Area delivery area.
- Outside it, or when the courier can’t take the address: 422 carrier_shipping_available. Ask the user. Only if they choose carrier shipping, send the address again with accept_carrier true.
- The quote’s delivery then shows method, city, fee_cents and, for shipping, transit_days. The address is not returned.
- The fee is charged at checkout as its own line, and doesn’t change after payment.
- Every quote needs a delivery price before it can be paid. A courier price is good for 24 hours.
Path
| Name | Type | Notes |
|---|---|---|
quote_id required | string |
Body · application/json
| Name | Type | Notes |
|---|---|---|
name required | string | ≤ 80 chars. |
phone required | string | ≤ 30 chars. |
street required | string | ≤ 120 chars. |
unit | string | ≤ 60 chars. |
city required | string | ≤ 80 chars. |
state required | string | Two letters. /^[A-Za-z]{2}$/. |
zip required | string | /^\d{5}(-\d{4})?$/. |
country | "US" | Default "US". |
notes | string | For the courier. ≤ 280 chars. |
accept_carrier | boolean | true only after the user chooses carrier shipping. Default false. |
Returns · 200
Errors
422 carrier_shipping_available | Outside the courier area. Ask the user, then send again with accept_carrier true. |
422 couriers_unavailable | No courier right now. Try again later. |
422 outside_delivery_area | No delivery to that address. |
422 outside_shipping_area | No carrier shipping to that address. |
422 shipping_review_required | Held for review by Sinter. |
422 delivery_invalid | Check the address and phone. |
502 | Courier service error. Retry. |
503 | Delivery is not available. |
POST/v1/quotes/{quote_id}/accept
Scope: purchaseAccepts a quote at one lead time. This is a purchase.
- Only with Allow purchases on for the key or agent, and the user’s approval of this purchase. See Rules for agents.
- Pick an option whose available is true.
- Every order is paid by card first: 402 payment_required with checkout_url. A person pays there; the quote becomes accepted once payment completes.
- A quote already paid: 200 and the order.
- Needs a delivery price: send delivery here, or call POST /v1/quotes/{quote_id}/delivery first.
Path
| Name | Type | Notes |
|---|---|---|
quote_id required | string |
Headers
| Name | Type | Notes |
|---|---|---|
Idempotency-Key | string | Required. The same key with the same request returns the first answer for 24 hours. A repeated 402 with an expired checkout link returns a new link. |
Body · application/json · optional
| Name | Type | Notes |
|---|---|---|
speed | asap | week | asap (Default) or week (Economy). Historical speed names are not accepted for new purchases. Default "week". |
delivery | object | Prices delivery in the same call; same fields as POST /v1/quotes/{quote_id}/delivery. |
name required | string | ≤ 80 chars. |
phone required | string | ≤ 30 chars. |
street required | string | ≤ 120 chars. |
unit | string | ≤ 60 chars. |
city required | string | ≤ 80 chars. |
state required | string | /^[A-Za-z]{2}$/. |
zip required | string | /^\d{5}(-\d{4})?$/. |
country | "US" | Default "US". |
notes | string | ≤ 280 chars. |
accept_carrier | boolean | Default false. |
Returns · 200
Errors
402 payment_required | checkout_url: give it to the user. |
403 | The key lacks the purchase scope. |
409 | Expired, held for review, status engineering, the option is no longer available, or delivery not priced. |
409 ship_date_changed | The Default ship date moved to a later day; ship_by has the new one. Accept again only if the user agrees. |
422 carrier_shipping_available | As for POST /v1/quotes/{quote_id}/delivery. |
428 | No Idempotency-Key. |
Orders
GET/v1/orders
Scope: ordersThe account’s accepted orders, newest first.
- limit: 1–100 (default 50). Pass next_cursor as cursor to read the next page; null means the last page.
Path
| Name | Type | Notes |
|---|---|---|
limit | integer | 1–100. |
cursor | string | ≤ 64 chars. |
Returns · 200
GET/v1/orders/{order_id}
Scope: ordersOne order. The order id is the quote id.
- Shipped more than one full business day (Monday to Friday, Pacific) after its ship-by date: 10% of parts plus corresponding tax is refunded. Delivery charges are excluded.
Path
| Name | Type | Notes |
|---|---|---|
order_id required | string |
Returns · 200
status | queued, in_production, on_hold, completed, cancelled. |
parts[] | unit, status and progress (0 to 1). |
ship_by | Promised ship date. null while ship_date_pending is true. |
paid | true once paid by card. |
tracking | Set once the order ships. |
delivery | method, city, fee_cents; transit_days and arrives_by for shipping; once packed, state, eta, delivered_at, carrier, tracking_number, tracking_url. |
POST/v1/orders/{order_id}/cancel
Scope: ordersPaid orders are committed and can't be cancelled; unpaid quotes are cancelled from the quote link.
- No refund once paid. If Sinter can't fulfill or declines an order, everything paid is refunded to the card, delivery and tax included.
- No partial cancellation.
Path
| Name | Type | Notes |
|---|---|---|
order_id required | string |
Returns · 200
Errors
409 | Paid orders can't be cancelled. Contact [email protected]. |
