New: quote and order photos, taste reports, shipped samples and ingredient comparison trials
Customer ordering: use recipe → fixed quote → checkout → Ordered - Pending Lab. Supported orders need no second lab confirmation. Test payments capture no funds. The private /lab-orders worksheet and research-intake protocols below are separate from customer checkout.
Customer service flow: POST /ideas → POST /ideas/{id}/generate → POST /order-quotes → POST /orders → GET /orders/{id}/results. Current quote and order guide. Internal /lab-orders below are a separate private bench workflow.
Ideas → recipes → orders.
REST API · MCP availability · Recipe quickstart
Create a recipe, get its quote, request a trial through /orders, then retrieve the recorded report. Use the quote and order guide for customer ordering. The advanced /lab-orders workflow below creates private bench records; it does not place a customer service order.
Connect
Create a key in the API tab. Its secret is shown once; only its hash is stored. Keys created in the interface expire in 90 days and can be revoked there. Keep credentials server-side.
This API accepts machine requests with your scoped key:
X-Deconfound-Api-Key: dcf_<your lab key>
Keep this credential server-side. No Sites gateway token is required. Only explicitly authorized operators can issue or use lab:write keys.
Browser requests use the signed-in Supabase session instead. Normal Authorization is not the lab key header. The API does not enable cross-origin browser access. The native API does not use ChatGPT login. Check HTTP status and content type before parsing a response.
| Scope | Allows |
|---|---|
ingredients:read | Search the bank and inspect basic ingredient profiles |
ideas:read / ideas:write | Retrieve / create and generate ideas |
orders:read / orders:write | Retrieve / create and cancel unstarted orders |
lab:write | Accept, start or abort runs; record and correct evidence |
The “Create & order” preset excludes lab permissions. Keys can be created, listed and revoked only from a signed-in workspace. Every record is isolated to its owner; unknown and other-owner IDs both return 404.
1. Send an idea and retrieve a result
To ask the AI to formulate from specific ingredients, include ingredientSelection. See the complete recipe quickstart for exact IDs, allowed and required ingredients, a runnable client and response handling. Read idea.recipes, the canonical array of complete candidates with quantities, proposed process steps and services.quote. Unfulfilled assessments return recipes:[], a reason and gaps; a successful assessment does not guarantee a recipe.
Persist the idea first, then run generation. This keeps a recoverable ID even if the model call or client connection fails. Save the returned ID and revision.
BASE=https://labs.deconfound.com/api/v2
curl --fail-with-body "$BASE/ideas" \
-H "X-Deconfound-Api-Key: $DECONFOUND_API_KEY" \
-H 'Content-Type: application/json' \
-H 'Idempotency-Key: idea-clear-lime-001' \
-d '{"prompt":"Completely clear lime juice","batchMassG":500}'{
"idea": {
"id": "<idea UUID>",
"status": "created",
"revision": 1,
"result": null,
"error": null
}
}Generate using that ID. Keep this foreground request open; it runs explore → finalize. Use a client timeout of 300 seconds. This is not a background job queue.
curl --fail-with-body --max-time 300 "$BASE/ideas/$IDEA_ID/generate" \
-H "X-Deconfound-Api-Key: $DECONFOUND_API_KEY" \
-H 'Content-Type: application/json' \
-H 'Idempotency-Key: generation-clear-lime-001' \
-d '{"revision":1}'GET /ideas/{id} exposes the current stage and saved result. Each complete idea.recipes entry contains ingredients, processSteps, requiresAcceptance and services.quote. Read idea.reason and idea.gaps for unfulfilled requests. The legacy feasibility object distinguishes:
| Fulfillment | Meaning |
|---|---|
| supported | A provisional recipe fits the ingredient bank |
| alternative | A feasible substitute, with an explicit compromise |
| development_required | A plausible experiment needs sourcing or method development; preserves the original identity, with no executable quantities |
| unsupported | A reason is returned; no executable recipe |
An unsupported idea is still a successful AI result. API failure is separate: idea.status is failed and idea.error explains it. Ideas may be created, running, succeeded or failed. A running attempt has a 240-second lease. On a later read, an expired attempt becomes GENERATION_INTERRUPTED. It may have incurred provider usage.
After a lost connection, retrieve the idea or replay the exact generation request with its original key. A running replay returns 202 and Retry-After. Terminal attempts replay their saved success or failure. To retry a failed attempt, retrieve the latest revision and use a new generation key. A succeeded idea cannot be regenerated; create another idea.
2. Quote and order a customer trial
Use POST /order-quotes with {"recipe":{"source":"idea","ideaId":"RETURNED_UUID","ideaRevision":3}}, substituting the current revision and an optional returned variantId. Submit the unchanged selection, returned quote.scopeHash as quoteHash, shareWithTeam:true, contactEmail and checkout:{acceptQuote:true} to POST /orders with a distinct idempotency key, only after agreement to the saved scope, total and trial terms. Unsupported or null-priced scope cannot be ordered. In test mode, after the customer confirms placement, call POST /orders/{id}/test-payment with {"outcome":"succeeded"} and a separate stable key. No money is charged. Success returns orderStatus:ordered_pending_lab (Ordered - Pending Lab); no lab offer is needed. Read /orders/{id}/results and its output for the published sample report. Full customer ordering and simulated checkout guide.
Advanced: private bench orders
Exact recipe + method: direct ingredient orders now accept preparation and optional benchSettings in the same request. Use ordered text instructions for a simple manual method, or typed steps for split additions and individual process targets. See the complete exact-recipe guide. The formula stays fixed; setup remains subject to lab review.
Order directly from a successful idea. Use its current revision; only set acceptAlternative when you want the stated substitute.
POST /api/v2/lab-orders
Idempotency-Key: order-clear-lime-001
Content-Type: application/json
{
"source": "idea",
"ideaId": "<idea UUID>",
"ideaRevision": 3,
"acceptAlternative": true,
"replicates": 2,
"tests": [
{"metric":"ph","min":3.0,"max":4.0},
{"metric":"turbidity","min":0,"max":1}
]
}These targets are examples, not validated specifications. For a direct ingredient order, first obtain the current catalogVersion from GET /ingredients:
{
"source": "ingredients",
"name": "Lime bench trial",
"catalogVersion": "<current catalogVersion>",
"batchMassG": 500,
"carbonated": false,
"ingredients": [
{"ingredientId":"flavor.lime","sourceMassG":0.6},
{"ingredientId":"acid.citric","sourceMassG":0.2}
],
"replicates": 1,
"tests": [{"metric":"ph","min":3.0,"max":4.0}]
}Quantities are grams of the selected commercial source product, including its carrier. They are not grams of pure aroma molecules. For direct ingredient orders, include base.water to specify its exact grams; all ingredients must then total batchMassG, or the API returns BATCH_MASS_MISMATCH. Omit water to calculate the remainder automatically. Set carbonated to request a reviewed carbonation process; do not add CO₂ lines. Custom working stocks, volume units and unverified density conversions are not accepted.
Each returned order preserves ingredient IDs, basic ingredient information and exact quantities per batch and across replicates. The lab retains product sourcing and qualification records privately. Ingredient profiles describe function, composition, carriers, allergens and compatibility where documented; they do not establish physical stock or an approved dose.
GET /lab-orders/{id} returns the same frozen manifest. It does not change when the ingredient bank changes. A new order from an old-version idea is rejected with CATALOG_CHANGED; generate a new idea. Same-key order replays still retrieve the original order.
Physical eligibility: A numeric quote may be planning-only. Enable checkout only when the quote response has review.eligible:true. Otherwise show review.reasons and keep the draft. Exact-product dose, application, allergen and process qualification is checked again when ordering; missing or expired evidence returns PHYSICAL_QUALIFICATION_REQUIRED. Qualified supported orders keep the normal fixed-price checkout flow.
Advanced: assemble and record
The Lab workspace guides the same API workflow: review → assemble → measure → results. The customer website orders through /order-quotes and /orders. A private bench order can use {"source":"generation","generationId":"<saved AI generation UUID>"}. The server retrieves the owned saved recipe; caller-supplied replacement ingredients are rejected.
New orders contain a frozen worksheet. Read GET /lab-orders/{id}/worksheet for exact steps, reviewed equipment, readiness holds and the next step for every batch. GET /processes describes supported and development-only operations. A development order preserves the requested concept, materials to source, gaps and proposed benchmarks. It cannot be accepted or started; establish a qualified formula and method, then create a new bench order.
If omitted, tests default to actual mass only with min:null,max:null. Both null means measurement only. Both numeric means a proposed acceptance range; these are not independently validated specifications.
All commands require the current order revision and an Idempotency-Key. The operator needs lab:write; cancellation needs orders:write.
Configure the actual bench
POST /lab-orders/{id}/actions
{
"action":"configure",
"revision":1,
"settings":{
"balanceId":"<actual instrument ID>",
"minimumWeighableMassG":"<verified positive number, g>",
"vessel":"<vessel and usable capacity>",
"mixer":"<equipment ID>",
"mixingMethod":"<reviewed method and equipment setting>",
"mixingSeconds":"<reviewed integer seconds>",
"temperatureC":"<reviewed numeric target>",
"temperatureToleranceC":"<reviewed numeric tolerance>",
"massTolerancePercent":"<reviewed numeric tolerance>",
"samplingMethod":"<aliquots and measurement preparation>",
"holdInstructions":"<conditions and disposition>"
}
}Replace numeric placeholders with JSON numbers. Quantities below the balance’s verified minimum block approval. Chilling requires chillMethod, chillTemperatureC and chillSeconds. Carbonation requires carbonationEquipment and a beverage-compatible carbonationProcedure. No generic pressure or dose is invented. Optional tests replaces the test plan before execution.
Any configuration change increments the worksheet version and invalidates approval. Settings cannot change after start. Review the AI-proposed process alongside the complete executable worksheet and qualify exact commercial products, intended use, lots, equipment and method.
POST /lab-orders/{id}/actions
{
"action":"accept",
"revision":"<current integer revision>",
"lots":{"<every ingredient ID including water>":"<reviewed lot>"},
"process":"<reviewed method reference>",
"note":"<product, dose, lot, equipment and method review>"
}
{"action":"start","revision":"<current integer revision>"}Acceptance binds the current worksheet hash. It records the operator’s review assertion, not independent certification. Each replicate must be assembled independently.
Record each step
POST /lab-orders/{id}/steps
{
"revision":"<current integer revision>",
"worksheetVersion":"<current integer worksheet version>",
"batchId":"<batch UUID>",
"stepId":"<next worksheet step ID>",
"actualMassG":"<numeric net grams actually added>",
"actualLot":"<actual product lot/container ID>"
}Addition steps require actual mass and lot. Mixing and chilling require numeric temperatureC, integer durationSeconds and observations in note. Setup, carbonation and finish require confirmed:true. Finish also requires numeric recoveredMassG, weighed before taking samples. Do not send evidence fields belonging to another step. The API rejects skipped steps, duplicate records, foreign batches and stale worksheet versions.
When a batch’s steps are complete, its preparation record is derived from the recorded additions and lots. Direct preparations writes are rejected for guided orders. Correct a transcription error with supersedesId, correctionReason and correctionType:"clerical". This explicitly asserts the physical batch did not change. Physical rework requires a new order and fresh evidence.
Measure and evaluate
POST /lab-orders/{id}/results
{
"revision":"<current integer revision>",
"measurements":[{
"batchId":"<assembled batch UUID>",
"metric":"ph","value":3.4,"unit":"pH",
"instrument":"<meter identifier>",
"method":"<actual method, temperature and sample handling>",
"calibration":"<calibration reference>",
"measuredAt":"<actual ISO 8601 UTC measurement time>"
}]
}The value above is an example, not a lab result. GET /lab-orders/{id}/results returns partial evidence, missing steps, missing readings, target checks, mass deviations and lot differences. Completion means every requested batch, step and reading is recorded. recorded means measurement-only values were captured; passed and failed refer to defined test targets. A target pass does not erase process or ingredient deviations, and no status is product release, shelf-life validation or a safety determination.
Measurement corrections use supersedesId and correctionReason; originals remain in the audit trail. Cancel before assembly, or abort accepted/testing orders with a reason. Aborted orders retain evidence and allow clerical corrections; new assembly steps are blocked. Pre-worksheet legacy orders retain their earlier preparation-record API and are labeled in the workspace.
| Metric | Unit | Input range |
|---|---|---|
| ph | pH | 0–14 |
| brix | °Bx | 0–85 |
| mass | g | 0–5,000 |
| conductivity | mS/cm | 0–500 |
| turbidity | NTU | 0–10,000 |
| sensory | score / 9 | 1–9 |
Retries and conflicts
Use a new Idempotency-Key for each distinct create, generation attempt or order command. Reuse it with the same body after an ambiguous network or storage failure. The key must be 8–128 characters: letters, digits, hyphens, underscores, periods or colons. Keys are scoped to the workspace and operation, retained with the record, and are not silently reused after a time window.
Normalized object-key order and default fields do not matter. Ingredient selection arrays are sorted as sets; other array order matters. A changed request under an existing key returns409 IDEMPOTENCY_CONFLICT. Successful replays include Idempotent-Replayed: true. Create replays return the current resource, while generation and order-command replays return the original saved command response. Read the resource separately for its latest revision.
Revisions prevent concurrent edits. On 409 REVISION_CONFLICT, fetch the latest record, reconsider the action, then send the new revision under a new key. Do not blindly retry invalid fields or unsupported ideas. API key creation is the exception: it is not idempotent because its secret is returned only once. If that response is lost, list your keys, revoke the orphaned key and create another.
Error contract
{
"error": {
"code": "INVALID_UNIT",
"message": "Use pH for ph.",
"retryable": false,
"requestId": "<request UUID>",
"details": []
}
}details and resourceId are optional. Every application response includes X-Request-Id and Cache-Control: no-store. Preserve the request ID when investigating a failure. Raw provider errors, stack traces and secrets are not returned. Retryable responses include Retry-After; preserve the original key while determining whether an operation committed.
| Status | Examples | Caller action |
|---|---|---|
| 400 | INVALID_JSON, INVALID_IDEMPOTENCY_KEY, INVALID_QUERY, INVALID_CURSOR, INVALID_RESOURCE_ID | Correct request syntax |
| 401 / 403 | AUTHENTICATION_REQUIRED, INVALID_API_KEY, INVALID_AUTH_HEADER, INSUFFICIENT_SCOPE, SESSION_REQUIRED, CROSS_ORIGIN_REQUEST | Fix credentials, permissions or call context |
| 404 / 405 | ENDPOINT_NOT_FOUND, IDEA_NOT_FOUND, ORDER_NOT_FOUND, GENERATION_NOT_FOUND, INGREDIENT_NOT_FOUND, KEY_NOT_FOUND, METHOD_NOT_ALLOWED | Check path, owner and Allow header |
| 409 | IDEMPOTENCY_CONFLICT, REVISION_CONFLICT, GENERATION_IN_PROGRESS, IDEA_ALREADY_GENERATED, GENERATION_SUPERSEDED, IDEA_NOT_READY, CATALOG_CHANGED, INGREDIENT_UNAVAILABLE, INVALID_ORDER_STATE, DEVELOPMENT_REQUIRED, WORKSHEET_NOT_READY, WORKSHEET_VERSION_CONFLICT, STEP_OUT_OF_SEQUENCE, STEP_ALREADY_RECORDED, ASSEMBLY_INCOMPLETE, RESULT_EXISTS, INVALID_CORRECTION, ORDER_HISTORY_LIMIT, ORDER_RECORD_LIMIT, API_KEY_LIMIT | Read the latest state and resolve the conflict |
| 413 / 415 | PAYLOAD_TOO_LARGE, UNSUPPORTED_MEDIA_TYPE | Keep JSON below 64 KiB; set application/json |
| 422 | VALIDATION_ERROR, PREPARATION_MASS_MISMATCH, PREPARATION_INGREDIENT_UNKNOWN, PREPARATION_CARBONATION_CONFLICT, INGREDIENT_SELECTION_CONFLICT, WORKSHEET_INCOMPLETE, ADDITION_EVIDENCE_REQUIRED, MIX_EVIDENCE_REQUIRED, RECOVERED_MASS_REQUIRED, STEP_CONFIRMATION_REQUIRED, UNEXPECTED_STEP_EVIDENCE, UNKNOWN_STEP, PREPARATION_FROM_STEPS, CLERICAL_CORRECTION_REQUIRED, UNSUPPORTED_IDEA, ALTERNATIVE_NOT_ACCEPTED, FORMULATION_NOT_READY, STOCK_NOT_SUPPORTED, INVALID_INGREDIENT_COUNT, DUPLICATE_INGREDIENT, AUTOMATIC_INGREDIENT, INVALID_MASS_PRECISION, BATCH_MASS_MISMATCH, MASS_BALANCE_EXCEEDED, UNKNOWN_INGREDIENT, INGREDIENT_REQUIRES_REVIEW, INGREDIENT_NOT_SOURCED, INVALID_LOTS, INVALID_ACTUAL_MASSES, UNKNOWN_BATCH, UNREQUESTED_METRIC, INVALID_UNIT, INVALID_MEASUREMENT, INVALID_MEASUREMENT_TIME, CORRECTION_REASON_REQUIRED, INVALID_CORRECTION, AI_REFUSED, AI_INVALID_RESULT | Resolve the named issue; schema details include field paths |
| 429 | RATE_LIMITED, AI_RATE_LIMITED | Wait before retrying |
| 502 / 503 / 504 | AI_BAD_RESPONSE, AI_UNAVAILABLE, AI_NOT_CONFIGURED, AI_TIMEOUT, GENERATION_INTERRUPTED, DEPENDENCY_UNAVAILABLE, STORAGE_UNAVAILABLE | Read saved state; retry with the documented key rules |
Limits and validation
- Early access: 10 AI attempts per visitor per rolling 24 hours, 1 running per person; 20 attempts and 2 running across the site. Browser and REST requests share these limits. Native MCP uses the same scoped API key. Paid customer service orders have no lifetime cap; at most five unpaid service orders may be open per account. Legacy private idea/bench-order/trial capacity remains separate. Same-key replays do not consume another slot.
- Prompts: 3–1,500 characters. Batch mass: 100–2,000 g. The private bench schema supports 1–20 distinct non-water ingredients plus optional water, 1–3 replicates and 1–6 unique test metrics. Automatic customer trials are limited to 500 g, eight non-water ingredients, three preparations and supported deliverables.
- Masses: positive, at least 0.0001 g, at most four decimal places. Ingredient sums cannot exceed batch mass; with explicit water, they must equal it exactly. This precision is a data limit, not a claim about balance capability.
- No unknown fields, duplicate ingredients, explicit CO₂ lines, deferred ingredients or unresolved dosing-review inputs. No negative, infinite or nonnumeric masses. Targets must have min≤max within the metric’s range, or both null for measurement-only checks.
- Maximum 18 measurements and 3 preparations in one result request. Each order retains up to 200 successful commands and 500 KiB of evidence. Measurement timestamps cannot precede the testing start or exceed the server time by more than 60 seconds.
- At most 20 active API keys per owner. The key list includes all active keys and recent inactive keys, up to 200 total. Programmatic session-based key creation supports expiry from 1–365 days.
GET /ideasandGET /lab-ordersuselimit(1–100, default 20) and opaquecursor. Results are newest first with a stable ID tie-breaker. Ingredients supportqandlimit(1–500).- External gateway, network and platform failures can occur before the application can issue this error envelope. No API can guarantee an exhaustive list of infrastructure failures.
← API keys · Machine-readable contract
Ingredient bank and scope
We formulate nonalcoholic drinks and water-based flavor evaluations. Cookie-inspired soda and banana-bread lemonade are in scope. Actual cookies, solid food, fermentation, distillation and new molecule synthesis are outside scope and return an unsupported result without a recipe.
The Library shows the active, documented ingredient bank. Bread, sweet cream and butter profiles describe aroma, not bread solids, dairy or texture. Nothing is marked purchased or qualified by being selected.
GET /api/v2/ingredients?bank=launch returns the shortlist and scope. Use bank=extended or bank=review, or omit the filter to search all ingredients. Native MCP also provides search_ingredients and get_ingredient with the same scoped key. See MCP connection examples. Each profile exposes its ingredient ID, name, short description and draftEligible status. The detail endpoint/tool includes available composition, carrier, allergen, compatibility and appearance information.
Omit ingredientSelection to let AI prefer launch candidates and select useful eligible extensions. Results identify these in result.brief.studio.ai.extendedIngredientIds. An explicit allowlist remains binding. Beverage requests default includeJuices to true; set false to prohibit juice. Flavor evaluation mode always excludes juice.
Real juice clarification, missing dairy/oat/cocoa matrices and unqualified dosing remain development requests. Development returns no executable recipe; its work order stays held. Unknown allergen or carrier suitability cannot be inferred from a flavor name. Tests require actual preparation and recorded readings.
Saved formulations and account access
GET /api/v2/formulations?limit=20 lists your saved website and API-generated recipes. Pass the returned nextCursor for another page. GET /api/v2/formulations/{id} reopens an owned recipe with its current service quote. Both require ideas:read. Create or revoke agent keys in API keys; keys identify their owner and cannot access other customers or the private admin site. Billing is not enabled.