API and webhooks
Open /settings/developerThe REST API lets another system read your catalogue and your orders, create orders, and push production status back. Webhooks push the same changes to you as they happen, so your system does not have to poll. Both are available on the Pro and Enterprise plans.
Getting a key
- 1
Open Settings, then the Developer tab.
If you see an upgrade notice instead, API access is not enabled for this team store yet.
- 2
Press Create key, give it a name, and choose its scopes.
Name it after the system that will use it, for example "Production ERP sync", so you can revoke the right one later. Grant only the scopes that system needs.
- 3
Copy the key immediately.
The full key is shown once and never again. Store it in your secret manager before closing the dialog. If you lose it, revoke the key and create another.
Keys belong to the team store, not to you
A key keeps working when the person who created it leaves the team. Any owner or admin can see and revoke the store's keys.
Scopes
| Field | What it does |
|---|---|
| read | Read products, categories, designs, configurations, quotes, orders, rosters, and production data. |
| quotes:create | Submit new quotes and orders. |
| quotes:write | Change order status, external references, and notes, and post messages on an order. |
| production:write | Move production jobs between stages. |
Making requests
Send the key as a bearer token on every request: Authorization: Bearer sk_live_.... The team store is taken from the key, so no request ever names it. The base URL is https://uniformbuilder.io/api.
Every endpoint, with the filters it accepts and the status codes it can return, is listed in the endpoint reference. Every response uses the same envelope. Success is { "success": true, "data": ... } and failure is { "success": false, "message": "...", "errors": [...] }. The full machine-readable specification is at /api/v1/openapi.json, which you can feed to a client generator.
Use the identifiers you already have
Detail endpoints accept either the internal id or the reference you hold: a product by its product_code (SKU), a design by its design_id, an order by its short id. GET /api/v1/products/TSHIRT-001 works exactly like the id form.
Lists are cursor paginated. Pass limit (up to 100) and follow next_cursor until has_more is false. Large fields are never included in a list: fetch a single record with ?include=items, ?include=config, or ?include=rows when you need them.
Orders and quotes
A submitted quote is an order. /api/v1/orders and /api/v1/quotes return the same records; the orders view adds an order_status field that combines the workflow status and the payment status into the single state most systems track.
To bring an order in from your own system, POST to /api/v1/quotes with a customer and one or more lines. Each line references a saved configuration by its id or short id, plus the quantity per size. The order then appears in Quotes and on the project tracker exactly like one submitted from the storefront.
To push status back, PATCH the order with a new status, your own external_order_id, or notes. Payment status is not writable: it is set by the payment providers so that a paid order always reflects money that actually moved.
Send an Idempotency-Key on writes
If a request times out you cannot tell whether it succeeded. Retrying with the same Idempotency-Key header replays the original response instead of creating a second order. Reusing a key with a different body is rejected.
Production files
Production jobs are the cards on your project tracker. GET /api/v1/production-jobs lists them with their stage name, and PATCH moves one to another stage, by column title or id.
Files come from two endpoints: /api/v1/quotes/{id}/files for documents filed against an order, such as the tech pack and roster bundle, and /api/v1/rosters/{id}/assets for the per-line cut files. Both return download links that expire after one hour, so fetch them when you are ready to download rather than storing them.
Webhooks
Register an endpoint under Settings > Developer, and we POST a JSON body to it whenever a subscribed event happens. The endpoint must be a public https URL. You get a signing secret once, at registration.
Events
| Field | What it does |
|---|---|
| quote.submitted | An order came in, from the storefront or through the API. |
| quote.status_changed | An order moved to a different workflow status. |
| quote.paid | Payment completed. |
| order.confirmed | The order reached a confirmed state. |
| design.approved | The order and its artwork were approved. |
| roster.locked | A team roster was frozen, so production files can be collected. |
| production.asset_ready | A per-line production file finished generating. |
| production.job_moved | A project tracker card moved to another stage. |
| ping | Test event, sent when you press Send test. |
Verifying a webhook
Every delivery carries three headers: webhook-id, webhook-timestamp, and webhook-signature. Compute an HMAC-SHA256 over the string {webhook-id}.{webhook-timestamp}.{raw request body} using your endpoint secret, base64 encode it, and compare it against the part of webhook-signature after the v1, prefix. Compare in constant time.
Check the timestamp too
Reject deliveries whose webhook-timestamp is more than a few minutes old. Without that check a captured request can be replayed against you later, because the signature itself stays valid.
Respond with any 2xx status as soon as you have stored the event. We treat anything else as a failure and retry with a growing delay: after about 30 seconds, then 2 minutes, 10 minutes, 1 hour, 4 hours, and 12 hours. An endpoint that keeps failing is switched off, and the Developer page shows why and offers to reactivate it.
Deduplicate on the event id
A retry reuses the same webhook-id, and an event sent to two of your endpoints shares it as well. Treat a repeated id as the same event rather than a new one.
Rate limits
Reads, writes, and order submissions have separate per-minute allowances, plus a daily ceiling. Your current limits are shown at the top of the Developer page and returned by GET /api/v1/me. Every response carries X-RateLimit-Limit and X-RateLimit-Remaining, and a 429 adds Retry-After telling you how long to wait.
Can I recover a key I lost?
No. Only a hash is stored, so nobody can read the key back, including us. Revoke it and create a new one.
What happens to my keys if I downgrade my plan?
The keys stay, but requests start returning 403 because API access is no longer enabled. Upgrading again restores them without any change on your side.
Why did I get a 404 for a record I know exists?
The record belongs to a different team store than the key does. We answer 404 rather than 403 so nobody can use the API to discover which ids exist elsewhere.
Can a key create another key, or register a webhook?
No. Both are done by a signed-in owner or admin in the dashboard. That keeps a leaked key from extending its own access.