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.
- Sign in with a customer account whose email address is verified, and open Dashboard → API keys.
- Name the key, choose its scopes, and confirm with your password.
- 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.XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXBase 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.
| Scope | Grants |
|---|---|
quotes | Estimate prices, upload and analyze files, create and reconfigure quotes. |
orders | Rate and select shipping, and issue a payment link a person opens to pay. |
The flow
- The account owner creates a key in the dashboard (API keys) and gives it to the agent.
- GET /options to learn what can be ordered; optionally POST /estimate for a quick price from geometry you already have.
- 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: *).
- POST /files/{id}/analyze: the server measures the part.
- POST /quotes with the analyzed file ids: a priced quote, standard and rush.
- PATCH /quotes/{id} to change material, color, infill, priority or quantities.
- POST /quotes/{id}/shipping-rates with the delivery address, then POST /quotes/{id}/shipping-selection with the chosen rate.
- 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.
| Endpoint | Scope | Charges | What it does |
|---|---|---|---|
| GET /me | quotes | - | 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 /options | quotes | - | 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 /estimate | quotes | estimate | Price 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 /files | quotes | upload upload_mib | Register 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}/analyze | quotes | analyze | Analyze 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 /quotes | quotes | - | This account's agent-created quotes, newest 50. Quotes created through the agent API on this account, newest first. |
| POST /quotes | quotes | quote_create | Create 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} | quotes | quote_update | Reconfigure 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-rates | orders | shipping_rate | Live 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-selection | orders | - | 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-link | orders | checkout_link | Issue 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.
| Action | Per day (account) | Per minute (key) | Site-wide per day | What counts |
|---|---|---|---|---|
request | 3,000 | 60 | - | Any authenticated call, reads included. |
estimate | 300 | 30 | - | Geometry-only price estimates (no file, nothing stored). |
upload | 40 | 10 | 2,000 | Upload URLs issued. |
upload_mib | 1,024 | - | 51,200 | Megabytes (MiB, rounded up per file) declared across all uploads. The upload URL enforces the exact size. |
analyze | 40 | 10 | 2,000 | File analyses (re-analyzing an analyzed file is free). |
quote_create | 20 | 5 | 1,000 | Quotes created. |
quote_update | 200 | 20 | - | Quote reconfigurations (each one re-prices). |
shipping_rate | 25 | 5 | 1,000 | Live carrier rate requests (repeating the same address within 6 hours is free). |
checkout_link | 100 | - | - | Payment links issued. |
| Cap | Value |
|---|---|
| Files per quote | 10 |
| Units per file line | 1000 |
| Units per quote (all lines) | 1000 |
| Parts per estimate | 10 |
| Mesh upload (.stl, .obj, .3mf) | 50 MiB |
| CAD upload (.step, .stp, .iges, .igs) | 25 MiB |
| Upload URL lifetime | 15 minutes |
| Uploads never quoted are deleted after | 7 days |
| Unpaid agent quotes open at once (quotes older than 14 days stop counting) | 25 |
| Live rate requests per quote per UTC day | 5 |
| Same-address rate reuse window (free) | 6 hours |
| Payment link validity (from when it is issued) | 14 days |
| Active keys per account | 5 |
Error codes
| Code | HTTP | Meaning |
|---|---|---|
UNAUTHORIZED | 401 | Missing, malformed, unknown or revoked API key. |
FORBIDDEN_SCOPE | 403 | The key does not carry the scope this endpoint needs. |
AGENT_API_DISABLED | 503 | The agent API is switched off site-wide. Try again later. |
RATE_LIMITED | 429 | A per-key or per-account budget is spent. See Retry-After. |
CAPACITY | 503 | A site-wide daily ceiling is reached. Try again after 00:00 UTC. |
VALIDATION | 400 (413 for a body over 64 KB) | The request body failed validation. See issues. |
NOT_FOUND | 404 | No such resource on this account. |
CONFLICT | 409 | The resource is not in a state that allows this. |
QUOTE_LOCKED | 409 | The quote is paid, price-locked, under review, or has a payment page open. |
FILE_NOT_UPLOADED | 409 | The upload URL was issued but no bytes arrived yet. |
FILE_SIZE_MISMATCH | 409 | The uploaded object is not the size that was declared. |
FILE_NOT_ANALYZED | 409 (not analyzed yet) or 422 (no printable volume found) | The file has not been analyzed yet, or analysis found no printable volume. |
OPEN_QUOTE_LIMIT | 409 | Too many unpaid agent quotes are open on this account. |
MATERIAL_UNAVAILABLE | 422 | That material, color or technology combination is not sold. |
PRICING_UNAVAILABLE | 422, or 409 on a quote with no payable price | This configuration cannot be priced automatically. |
REQUIRES_REVIEW | 409 | A part exceeds the build volume; a person must price it. |
SHIPPING_UNAVAILABLE | 503 | Shipping rates could not be fetched. |
IDEMPOTENCY_IN_PROGRESS | 409 | A request with this Idempotency-Key is still being processed. |
INTERNAL | 500, or 503 with Retry-After | Something 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 itsx-limitsextension. - 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.