Skip to main content

Agent API

Quotes and orders for AI agents

An AI agent holding a key from a 3D Services USA customer can price parts, upload and analyze CAD files, build quotes, rate shipping and prepare the order. Prices come from the same engine checkout uses, computed from our own measurement of the part, never from numbers the agent sends.

The agent cannot pay. The last step returns a link; the account owner signs in, reviews the order and pays.

Get a key

Agents cannot register themselves. The account owner creates the key, because a key spends money on the account's behalf (storage, analysis, live carrier rates) and prepares orders that account pays for.

  1. Sign in with a customer account whose email address is verified, and open Dashboard → API keys.
  2. Name the key, choose its scopes, and confirm with your password.
  3. Copy the token into your agent. It is shown once; a lost token is revoked and replaced.

Up to 5 active keys per account. Revoke a key from the same page; every key is revoked automatically when the account password is changed or reset.

Authentication

Send the token on every request, in the Authorization header and nowhere else:

Authorization: Bearer agk_XXXXXXXXXXXXXXXX.XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX

Base URL: https://3dservicesusa.com/api/agent/v1. Every authentication failure answers the same 401 UNAUTHORIZED, whatever the cause. A call outside the key's scopes answers 403 FORBIDDEN_SCOPE.

Key scopes
ScopeGrants
quotesEstimate prices, upload and analyze files, create and reconfigure quotes.
ordersRate and select shipping, and issue a payment link a person opens to pay.

The flow

  1. The account owner creates a key in the dashboard (API keys) and gives it to the agent.
  2. GET /options to learn what can be ordered; optionally POST /estimate for a quick price from geometry you already have.
  3. POST /files to register an upload, then PUT the bytes once to the returned URL with the returned headers (the declared Content-Length and If-None-Match: *).
  4. POST /files/{id}/analyze: the server measures the part.
  5. POST /quotes with the analyzed file ids: a priced quote, standard and rush.
  6. PATCH /quotes/{id} to change material, color, infill, priority or quantities.
  7. POST /quotes/{id}/shipping-rates with the delivery address, then POST /quotes/{id}/shipping-selection with the chosen rate.
  8. POST /quotes/{id}/checkout-link and give the returned payUrl to the account owner. They sign in, review and pay. The agent cannot pay.

A quote with a part larger than our build volume is still created, marked requiresManualReview; it cannot be paid until our team prices it. Creating endpoints accept an Idempotency-Key header, so a retried request returns the first result at no charge.

Endpoints

Paths are relative to /api/agent/v1. {id} on a quote accepts its id or its quote number. Every call is also charged to request.

Agent API endpoints
EndpointScopeChargesWhat it does
GET /mequotes-The key, its account, today's usage and every limit. Who this key acts for, which scopes it carries, how much of each daily budget the account has used today, and the limits themselves. Daily budgets reset at resetsAt (00:00 UTC).
GET /optionsquotes-What can be ordered. Technologies, the materials each one offers and the colors each material is produced in, the infill ladder, priorities and the upload extensions. Send these strings exactly as listed.
POST /estimatequotesestimatePrice geometry you describe, at both priorities. Nothing is stored. An estimate from caller-supplied volume, surface area and bounding box, priced by the same engine as a real quote. It is not binding: the only binding price is one computed from a file the server analyzed. Excludes shipping and tax.
POST /filesquotesupload upload_mibRegister an upload and get a presigned PUT URL. Declare the filename and the exact size in bytes. The response carries a URL to PUT the bytes to; send exactly the headers in upload.headers. The declared Content-Length is part of the signature, so the storage service refuses a body of any other size, and the signed If-None-Match: * makes the upload write-once: a second PUT to the same file is refused with 412. The URL expires after expiresAt.
GET /files/{id}quotes-An uploaded file's status and server-measured geometry. awaiting_upload until analysis has run; then analyzed (with the geometry we measured) or failed (with the reason).
POST /files/{id}/analyzequotesanalyzeAnalyze the uploaded bytes. Reads the object you uploaded and measures it. Calling it again on an analyzed file returns the stored analysis at no charge. Nothing is charged when the bytes have not arrived or are not the declared size.
GET /quotesquotes-This account's agent-created quotes, newest 50. Quotes created through the agent API on this account, newest first.
POST /quotesquotesquote_createCreate a priced quote from analyzed files. Every file must be analyzed and owned by this account. The price is computed from the geometry the server measured, never from numbers in the request. A part larger than the build volume still creates a quote, marked requiresManualReview and not payable until our team prices it.
GET /quotes/{id}quotes-One quote on this account, by id or quote number. Any quote owned by the account, including ones the owner made in a browser. capabilities says what may still be changed and why not.
PATCH /quotes/{id}quotesquote_updateReconfigure and re-price. Change technology, material, color, infill, priority or the quantities of files already on the quote. Changing the technology, the material or any quantity clears a selected shipping rate, because the parcel changed; the address is kept.
POST /quotes/{id}/shipping-ratesordersshipping_rateLive carrier rates for a US address. The parcel is built from the server's own geometry. Asking again for the same address and recipient within the reuse window returns the stored rates at no charge. A quote with no parts answers 409.
POST /quotes/{id}/shipping-selectionorders-Choose one of the rated options. rateId must be one of the rates returned by the most recent shipping-rates call for this quote.
POST /quotes/{id}/checkout-linkorderscheckout_linkIssue the link a person opens to sign in, review and pay. Returns payUrl, a page on the account dashboard. The account owner signs in, reviews the order and pays. The agent cannot complete payment, and no payment credential or access token is ever returned to it.

Responses

Every response is JSON in one envelope, and every one is Cache-Control: no-store. Branch on error.code, never on the message.

{ "ok": true,  "data": { ... } }
{ "ok": false, "error": { "code": "QUOTE_LOCKED", "message": "...", "reason": "checkout_open" } }

Request bodies are strict: an unknown field is refused, not ignored. In particular the API never accepts geometry for a quote, a parcel, a shipping cost or a tax figure; those come from our own measurement and our carriers.

Limits

Budgets are per account (all of its keys together) per UTC day, resetting at 00:00 UTC; the per-minute burst is per key. A spent budget answers 429 RATE_LIMITED with Retry-After. A site-wide ceiling answers 503 CAPACITY for everyone until the day rolls over. GET /me reports today's usage.

Budgets per action
ActionPer day (account)Per minute (key)Site-wide per dayWhat counts
request3,00060-Any authenticated call, reads included.
estimate30030-Geometry-only price estimates (no file, nothing stored).
upload40102,000Upload URLs issued.
upload_mib1,024-51,200Megabytes (MiB, rounded up per file) declared across all uploads. The upload URL enforces the exact size.
analyze40102,000File analyses (re-analyzing an analyzed file is free).
quote_create2051,000Quotes created.
quote_update20020-Quote reconfigurations (each one re-prices).
shipping_rate2551,000Live carrier rate requests (repeating the same address within 6 hours is free).
checkout_link100--Payment links issued.
Hard caps
CapValue
Files per quote10
Units per file line1000
Units per quote (all lines)1000
Parts per estimate10
Mesh upload (.stl, .obj, .3mf)50 MiB
CAD upload (.step, .stp, .iges, .igs)25 MiB
Upload URL lifetime15 minutes
Uploads never quoted are deleted after7 days
Unpaid agent quotes open at once (quotes older than 14 days stop counting)25
Live rate requests per quote per UTC day5
Same-address rate reuse window (free)6 hours
Payment link validity (from when it is issued)14 days
Active keys per account5

Error codes

Error codes
CodeHTTPMeaning
UNAUTHORIZED401Missing, malformed, unknown or revoked API key.
FORBIDDEN_SCOPE403The key does not carry the scope this endpoint needs.
AGENT_API_DISABLED503The agent API is switched off site-wide. Try again later.
RATE_LIMITED429A per-key or per-account budget is spent. See Retry-After.
CAPACITY503A site-wide daily ceiling is reached. Try again after 00:00 UTC.
VALIDATION400 (413 for a body over 64 KB)The request body failed validation. See issues.
NOT_FOUND404No such resource on this account.
CONFLICT409The resource is not in a state that allows this.
QUOTE_LOCKED409The quote is paid, price-locked, under review, or has a payment page open.
FILE_NOT_UPLOADED409The upload URL was issued but no bytes arrived yet.
FILE_SIZE_MISMATCH409The uploaded object is not the size that was declared.
FILE_NOT_ANALYZED409 (not analyzed yet) or 422 (no printable volume found)The file has not been analyzed yet, or analysis found no printable volume.
OPEN_QUOTE_LIMIT409Too many unpaid agent quotes are open on this account.
MATERIAL_UNAVAILABLE422That material, color or technology combination is not sold.
PRICING_UNAVAILABLE422, or 409 on a quote with no payable priceThis configuration cannot be priced automatically.
REQUIRES_REVIEW409A part exceeds the build volume; a person must price it.
SHIPPING_UNAVAILABLE503Shipping rates could not be fetched.
IDEMPOTENCY_IN_PROGRESS409A request with this Idempotency-Key is still being processed.
INTERNAL500, or 503 with Retry-AfterSomething failed on our side.

Examples

$AGENT_KEY holds the token; $FILE_ID, $QUOTE and $UPLOAD_URL come from earlier responses.

Check the key and today's usage

curl -s https://3dservicesusa.com/api/agent/v1/me -H "Authorization: Bearer $AGENT_KEY"

Register an upload (the size is the file's exact byte count)

curl -s -X POST https://3dservicesusa.com/api/agent/v1/files -H "Authorization: Bearer $AGENT_KEY" -H "Content-Type: application/json" \
  -H "Idempotency-Key: bracket-v3-upload" \
  -d '{"filename":"bracket.stl","sizeBytes":482113}'

Upload the bytes once (curl sets Content-Length from the file; If-None-Match is signed, and a second PUT gets 412)

curl -s --upload-file bracket.stl -H "If-None-Match: *" "$UPLOAD_URL"

Analyze it

curl -s -X POST https://3dservicesusa.com/api/agent/v1/files/$FILE_ID/analyze -H "Authorization: Bearer $AGENT_KEY"

Create a quote

curl -s -X POST https://3dservicesusa.com/api/agent/v1/quotes -H "Authorization: Bearer $AGENT_KEY" -H "Content-Type: application/json" \
  -H "Idempotency-Key: bracket-v3-quote" \
  -d '{"files":[{"fileId":"'$FILE_ID'","quantity":4}],"technology":"FDM","material":"PETG","color":"Black","infill":20}'

Rate shipping to a US address

curl -s -X POST https://3dservicesusa.com/api/agent/v1/quotes/$QUOTE/shipping-rates -H "Authorization: Bearer $AGENT_KEY" -H "Content-Type: application/json" \
  -d '{"address":{"name":"Dana Smith","street1":"100 Main St","city":"Austin","state":"TX","zip":"78701"}}'

Select a rate

curl -s -X POST https://3dservicesusa.com/api/agent/v1/quotes/$QUOTE/shipping-selection -H "Authorization: Bearer $AGENT_KEY" -H "Content-Type: application/json" \
  -d '{"rateId":"rate_..."}'

Get the link the account owner opens to pay

curl -s -X POST https://3dservicesusa.com/api/agent/v1/quotes/$QUOTE/checkout-link -H "Authorization: Bearer $AGENT_KEY"

Machine-readable descriptions

  • OpenAPI 3.1 document (/agents/openapi.json), with every limit in its x-limits extension.
  • auth.md: how an agent obtains and uses a key.
  • API catalog (RFC 9727, /.well-known/api-catalog).
  • Any public page on this site is also available as markdown: request it with Accept: text/markdown.

Questions or a higher limit for a real workload: contact us.