{"openapi":"3.1.0","info":{"title":"Bloomit API","version":"0.0.1","description":"The official API for Bloomit, an iOS application focused on leveraging artificial intelligence to generate personalized coffee recipes."},"servers":[{"url":"https://api.bloomit.whatsinlab.com"}],"components":{"schemas":{"CreateAuthenticationResponse":{"type":"object","properties":{"data":{"type":"object","properties":{"type":{"type":"string","enum":["authentications"],"description":"Resource type identifier."},"id":{"type":"string","description":"Unique resource identifier."},"attributes":{"type":"object","properties":{"sessionToken":{"type":"string","description":"The opaque bearer session token for subsequent API requests."},"expiresAt":{"type":"string","description":"The session expiration time in ISO 8601 format."},"user":{"type":"object","properties":{"id":{"type":"string","description":"The server-side user identifier."},"appleSub":{"type":"string","description":"The stable Apple Account subject identifier."},"email":{"type":["string","null"],"description":"The user's email address when Apple shares it."},"emailVerified":{"type":"boolean","description":"Whether Apple verified the shared email address."},"isPrivateEmail":{"type":"boolean","description":"Whether Apple shared a private relay email address."}},"required":["id","appleSub","email","emailVerified","isPrivateEmail"]}},"required":["sessionToken","expiresAt","user"],"description":"Resource attributes."}},"required":["type","id","attributes"],"description":"JSON:API resource object of type \"authentications\"."},"links":{"$ref":"#/components/schemas/Links"}},"required":["data"],"description":"JSON:API success response."},"Links":{"type":"object","properties":{"self":{"type":"string","description":"The URL that generated this response."}},"required":["self"],"description":"JSON:API links object."},"ErrorResponse":{"type":"object","properties":{"errors":{"type":"array","items":{"$ref":"#/components/schemas/Error"}}},"required":["errors"],"description":"JSON:API error response."},"Error":{"type":"object","properties":{"id":{"type":"string","description":"Unique identifier for this error occurrence."},"status":{"type":"string","description":"HTTP status code as a string."},"code":{"type":"string","description":"Application-specific error code."},"title":{"type":"string","description":"Short, human-readable summary of the error."},"detail":{"type":"string","description":"Detailed explanation of the error."},"source":{"$ref":"#/components/schemas/ErrorSource"}},"required":["id","status","code","title"],"description":"JSON:API error object."},"ErrorSource":{"type":"object","properties":{"pointer":{"type":"string","description":"JSON pointer to the field that caused the error."},"parameter":{"type":"string","description":"Query parameter that caused the error."}},"description":"Source of the error."},"CreateAuthenticationRequest":{"type":"object","properties":{"identityToken":{"type":"string","description":"The identity token returned by Sign in with Apple."},"nonce":{"type":"string","description":"The raw nonce used to start Sign in with Apple. The server hashes it and compares it to the nonce claim in the identity token."},"appAttestKeyId":{"type":"string","description":"Optional App Attest key identifier to associate with the signed-in user."}},"required":["identityToken","nonce"]},"DeleteAuthenticationResponse":{"type":"object","properties":{"data":{"type":"object","properties":{"type":{"type":"string","enum":["authentications"],"description":"Resource type identifier."},"id":{"type":"string","description":"Unique resource identifier."},"attributes":{"type":"object","properties":{"revoked":{"type":"boolean","description":"Whether the current authentication session was revoked."}},"required":["revoked"],"description":"Resource attributes."}},"required":["type","id","attributes"],"description":"JSON:API resource object of type \"authentications\"."},"links":{"$ref":"#/components/schemas/Links"}},"required":["data"],"description":"JSON:API success response."},"ChallengeResponse":{"type":"object","properties":{"data":{"type":"object","properties":{"type":{"type":"string","enum":["challenges"],"description":"Resource type identifier."},"id":{"type":"string","description":"Unique resource identifier."},"attributes":{"type":"object","properties":{"challenge":{"type":"string","description":"A hex-encoded one-time challenge."}},"required":["challenge"],"description":"Resource attributes."}},"required":["type","id","attributes"],"description":"JSON:API resource object of type \"challenges\"."},"links":{"$ref":"#/components/schemas/Links"}},"required":["data"],"description":"JSON:API success response."},"RegisterAttestationResponse":{"type":"object","properties":{"data":{"type":"object","properties":{"type":{"type":"string","enum":["attestations"],"description":"Resource type identifier."},"id":{"type":"string","description":"Unique resource identifier."},"attributes":{"type":"object","properties":{"keyId":{"type":"string","description":"The registered App Attest key identifier."},"registeredAt":{"type":"string","description":"The registration time in ISO 8601 format."}},"required":["keyId","registeredAt"],"description":"Resource attributes."}},"required":["type","id","attributes"],"description":"JSON:API resource object of type \"attestations\"."},"links":{"$ref":"#/components/schemas/Links"}},"required":["data"],"description":"JSON:API success response."},"RegisterAttestationRequest":{"type":"object","properties":{"attestation":{"type":"string","description":"A Base64-encoded App Attest attestation object."},"challenge":{"type":"string","description":"The challenge returned by the challenge request."}},"required":["attestation","challenge"]},"AnalyzeBeanProfileResponse":{"type":"object","properties":{"data":{"type":"object","properties":{"type":{"type":"string","enum":["beans"],"description":"Resource type identifier."},"id":{"type":"string","description":"Unique resource identifier."},"attributes":{"type":"object","properties":{"bean":{"$ref":"#/components/schemas/Bean"}},"required":["bean"],"description":"Resource attributes."}},"required":["type","id","attributes"],"description":"JSON:API resource object of type \"beans\"."},"links":{"$ref":"#/components/schemas/Links"}},"required":["data"],"description":"JSON:API success response."},"Bean":{"type":"object","properties":{"brand":{"type":["string","null"],"minLength":1,"description":"Brand or roaster name shown on the coffee packaging."},"name":{"type":["string","null"],"minLength":1,"description":"Product name of the coffee bean."},"varieties":{"type":"array","items":{"type":"string","minLength":1,"description":"Coffee variety or cultivar wording shown on the packaging or supported by evidence."},"default":[],"description":"Coffee varieties or cultivars when visible on the packaging or supported by evidence."},"processing":{"oneOf":[{"type":"object","properties":{"method":{"type":"string","enum":["washed"]},"label":{"type":["string","null"],"minLength":1,"description":"Original or more specific processing wording from the bag."}},"required":["method"]},{"type":"object","properties":{"method":{"type":"string","enum":["natural"]},"label":{"type":["string","null"],"minLength":1,"description":"Original or more specific processing wording from the bag."}},"required":["method"]},{"type":"object","properties":{"method":{"type":"string","enum":["honey"]},"label":{"type":["string","null"],"minLength":1,"description":"Original or more specific processing wording from the bag."}},"required":["method"]},{"type":"object","properties":{"method":{"type":"string","enum":["anaerobic"]},"label":{"type":["string","null"],"minLength":1,"description":"Original or more specific processing wording from the bag."}},"required":["method"]},{"type":"object","properties":{"method":{"type":"string","enum":["other"]},"label":{"type":"string","minLength":1,"description":"Processing wording from the source when it does not fit the canonical method set."}},"required":["method","label"]},{"type":"null"}],"description":"Processing details for the bean when known."},"roastLevel":{"type":"string","enum":["light","lightMedium","medium","mediumDark","dark"],"description":"Roast level as extracted from the source."},"roastStyle":{"type":"string","enum":["filter","espresso","omni"],"description":"Preferred brew-style target for the roast when stated by the roaster."},"decaffeinationMethod":{"oneOf":[{"type":"object","properties":{"method":{"type":"string","enum":["swissWater"]}},"required":["method"]},{"type":"object","properties":{"method":{"type":"string","enum":["mountainWater"]}},"required":["method"]},{"type":"object","properties":{"method":{"type":"string","enum":["eaSugarcane"]}},"required":["method"]},{"type":"object","properties":{"method":{"type":"string","enum":["co2"]}},"required":["method"]},{"type":"object","properties":{"method":{"type":"string","enum":["other"]},"label":{"type":"string","minLength":1,"description":"Decaffeination wording from the source when it does not fit the canonical method set."}},"required":["method","label"]}],"description":"Decaffeination details. Omit when the bean is not decaf."},"type":{"type":"string","enum":["singleOrigin","blend"],"description":"Whether the coffee is a single origin or a blend."},"tastingNotes":{"type":"array","items":{"type":"string","minLength":1},"default":[],"description":"Concise tasting notes from the bag or product description, aligned with the SCA Coffee Taster's Flavor Wheel when practical."}},"description":"Canonical coffee bean profile shared across routes and AI extraction.","required":["varieties","tastingNotes"]},"AnalyzeBeanProfileRequest":{"type":"object","properties":{"image":{"type":"string","format":"binary","description":"The coffee bag image to analyze."},"locale":{"$ref":"#/components/schemas/Locale"}},"description":"Multipart form data containing the coffee bag image."},"Locale":{"type":"string","default":"en-US","description":"Output locale for localized user-facing content (BCP 47 format)."},"ExtractBeanDataSSEEvent":{"oneOf":[{"type":"object","properties":{"event":{"type":"string","enum":["status"]},"data":{"$ref":"#/components/schemas/SSEStatusEvent"},"id":{"type":"string","description":"Auto-incrementing stream event identifier."}},"required":["event","data","id"]},{"type":"object","properties":{"event":{"type":"string","enum":["delta"]},"data":{"$ref":"#/components/schemas/SSEDeltaEvent"},"id":{"type":"string","description":"Auto-incrementing stream event identifier."}},"required":["event","data","id"]},{"type":"object","properties":{"event":{"type":"string","enum":["done"]},"data":{"$ref":"#/components/schemas/SSEDoneEvent"},"id":{"type":"string","description":"Auto-incrementing stream event identifier."}},"required":["event","data","id"]},{"type":"object","properties":{"event":{"type":"string","enum":["complete"]},"data":{"$ref":"#/components/schemas/ExtractBeanDataResult"},"id":{"type":"string","description":"Auto-incrementing stream event identifier."}},"required":["event","data","id"]},{"type":"object","properties":{"event":{"type":"string","enum":["error"]},"data":{"$ref":"#/components/schemas/SSEErrorEvent"},"id":{"type":"string","description":"Auto-incrementing stream event identifier."}},"required":["event","data","id"]}]},"SSEStatusEvent":{"type":"object","properties":{"message":{"type":"string"}},"required":["message"]},"SSEDeltaEvent":{"type":"object","properties":{"path":{"type":"string"},"value":{}},"required":["path"]},"SSEDoneEvent":{"type":"object","properties":{"message":{"type":"string"}},"required":["message"]},"ExtractBeanDataResult":{"type":"object","properties":{"bean":{"$ref":"#/components/schemas/Bean"}},"required":["bean"]},"SSEErrorEvent":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"},"phase":{"type":"string","enum":["streaming","parsing"]}},"required":["code","message"]},"InlineImagePayload":{"type":"object","properties":{"data":{"type":"string","minLength":1,"description":"Base64-encoded image payload."},"mimeType":{"type":"string","description":"Image MIME type. Defaults to image/jpeg when omitted."}},"required":["data"]},"RefineBeanDataSSEEvent":{"oneOf":[{"type":"object","properties":{"event":{"type":"string","enum":["status"]},"data":{"$ref":"#/components/schemas/SSEStatusEvent"},"id":{"type":"string","description":"Auto-incrementing stream event identifier."}},"required":["event","data","id"]},{"type":"object","properties":{"event":{"type":"string","enum":["delta"]},"data":{"$ref":"#/components/schemas/SSEDeltaEvent"},"id":{"type":"string","description":"Auto-incrementing stream event identifier."}},"required":["event","data","id"]},{"type":"object","properties":{"event":{"type":"string","enum":["done"]},"data":{"$ref":"#/components/schemas/SSEDoneEvent"},"id":{"type":"string","description":"Auto-incrementing stream event identifier."}},"required":["event","data","id"]},{"type":"object","properties":{"event":{"type":"string","enum":["complete"]},"data":{"$ref":"#/components/schemas/RefineBeanDataResult"},"id":{"type":"string","description":"Auto-incrementing stream event identifier."}},"required":["event","data","id"]},{"type":"object","properties":{"event":{"type":"string","enum":["error"]},"data":{"$ref":"#/components/schemas/SSEErrorEvent"},"id":{"type":"string","description":"Auto-incrementing stream event identifier."}},"required":["event","data","id"]}]},"RefineBeanDataResult":{"type":"object","properties":{"refinedBean":{"$ref":"#/components/schemas/Bean"},"sources":{"type":"array","items":{"$ref":"#/components/schemas/BloomySearchResult"}}},"required":["refinedBean","sources"]},"BloomySearchResult":{"type":"object","properties":{"title":{"type":"string"},"url":{"type":"string","format":"uri"},"snippet":{"type":"string"}},"required":["title","url","snippet"]},"ExtractAndRefineBeanSSEEvent":{"oneOf":[{"type":"object","properties":{"event":{"type":"string","enum":["status"]},"data":{"$ref":"#/components/schemas/SSEStatusEvent"},"id":{"type":"string","description":"Auto-incrementing stream event identifier."}},"required":["event","data","id"]},{"type":"object","properties":{"event":{"type":"string","enum":["delta"]},"data":{"$ref":"#/components/schemas/SSEDeltaEvent"},"id":{"type":"string","description":"Auto-incrementing stream event identifier."}},"required":["event","data","id"]},{"type":"object","properties":{"event":{"type":"string","enum":["done"]},"data":{"$ref":"#/components/schemas/SSEDoneEvent"},"id":{"type":"string","description":"Auto-incrementing stream event identifier."}},"required":["event","data","id"]},{"type":"object","properties":{"event":{"type":"string","enum":["complete"]},"data":{"$ref":"#/components/schemas/ExtractAndRefineBeanResult"},"id":{"type":"string","description":"Auto-incrementing stream event identifier."}},"required":["event","data","id"]},{"type":"object","properties":{"event":{"type":"string","enum":["error"]},"data":{"$ref":"#/components/schemas/SSEErrorEvent"},"id":{"type":"string","description":"Auto-incrementing stream event identifier."}},"required":["event","data","id"]}]},"ExtractAndRefineBeanResult":{"type":"object","properties":{"bean":{"$ref":"#/components/schemas/Bean"},"refinedBean":{"$ref":"#/components/schemas/Bean"},"sources":{"type":"array","items":{"$ref":"#/components/schemas/BloomySearchResult"}}},"required":["bean","refinedBean","sources"]},"RecipeResponse":{"type":"object","properties":{"data":{"type":"object","properties":{"type":{"type":"string","enum":["recipes"],"description":"Resource type identifier."},"id":{"type":"string","description":"Unique resource identifier."},"attributes":{"type":"object","properties":{"bean":{"$ref":"#/components/schemas/Bean"},"recipe":{"$ref":"#/components/schemas/Recipe"},"preferences":{"$ref":"#/components/schemas/Preferences"}},"required":["bean","recipe","preferences"],"description":"Resource attributes."}},"required":["type","id","attributes"],"description":"JSON:API resource object of type \"recipes\"."},"links":{"$ref":"#/components/schemas/Links"}},"required":["data"],"description":"JSON:API success response."},"Recipe":{"type":"object","properties":{"title":{"type":"string","minLength":1,"description":"Title of the recipe."},"description":{"type":"string","minLength":1,"description":"Description explaining the recipe rationale."},"coffee":{"$ref":"#/components/schemas/Coffee"},"pours":{"type":"array","items":{"$ref":"#/components/schemas/Pour"},"minItems":1,"description":"Ordered pour steps to brew the recipe."}},"required":["title","description","coffee","pours"],"description":"Complete pour-over recipe tailored to the analyzed beans."},"Coffee":{"type":"object","properties":{"dose":{"type":"number","minimum":5,"maximum":18,"description":"Coffee dose in grams (g)."},"coffeeWaterRatio":{"type":"number","minimum":5,"maximum":25,"default":15,"description":"Parts of water per one part coffee (5-25, in 0.5 increments)."},"grinding":{"$ref":"#/components/schemas/CoffeeGrinding"}},"required":["dose","grinding"],"description":"Defines the coffee dose and grinder configuration."},"CoffeeGrinding":{"type":"object","properties":{"size":{"type":"integer","minimum":1,"maximum":80,"description":"xBloom Studio grinder setting (1=espresso fine, 80=cold brew coarse)."},"speed":{"type":"integer","minimum":60,"maximum":120,"description":"Grinder RPM between 60 and 120, in 10 RPM increments.","multipleOf":10}},"required":["size","speed"],"description":"Details the grinder configuration used to prepare the grounds."},"Pour":{"type":"object","properties":{"volume":{"type":"number","minimum":0,"maximum":240,"description":"Water volume for this pour in milliliters (mL)."},"temperature":{"$ref":"#/components/schemas/Temperature"},"flowRate":{"type":"number","minimum":3,"maximum":3.5,"description":"Pour flow rate in milliliters per second (mL/s)."},"pause":{"type":"number","minimum":0,"maximum":59,"description":"Pause duration after this pour in seconds (s)."},"style":{"$ref":"#/components/schemas/PourStyle"},"agitation":{"type":"string","enum":["before","after","both"],"description":"Optional agitation timing for this pour. Omit the field when no agitation is used."}},"required":["volume","temperature","flowRate","pause","style"],"description":"Captures a single pour step including volume, temperature, flow, and optional agitation."},"Temperature":{"anyOf":[{"type":"string","enum":["rt"]},{"type":"string","enum":["bt"]},{"type":"integer","minimum":40,"maximum":95}],"description":"The water temperature setting for a pour step. Use ``rt`` for room temperature, ``bt`` for boiling temperature, or an integer Celsius value from 40 to 95."},"PourStyle":{"type":"string","enum":["centered","spiral","circular"],"description":"The style of pouring water over the coffee grounds."},"Preferences":{"type":"object","properties":{"iced":{"type":"boolean","default":false,"description":"Adjust the recipe for iced coffee."},"taste":{"type":"object","properties":{"strength":{"type":"number","minimum":0,"maximum":1,"default":0.5,"description":"Strength/TDS (0=light/tea-like, 1=syrupy/heavy). Primarily affects coffee-to-water ratio."},"balance":{"type":"number","minimum":0,"maximum":1,"default":0.5,"description":"Extraction profile (0=bright/zesty, 1=deep/rounded). Affects water temperature and contact time."},"body":{"type":"number","minimum":0,"maximum":1,"default":0.5,"description":"Texture/body (0=lean/delicate, 1=rich/full-bodied). Affects grind size and pour stages."}},"default":{"strength":0.5,"balance":0.5,"body":0.5},"description":"Taste profile preferences."}},"description":"User preferences for fine-tuning the generated recipe."},"RecipeSSEEvent":{"oneOf":[{"type":"object","properties":{"event":{"type":"string","enum":["status"]},"data":{"$ref":"#/components/schemas/SSEStatusEvent"},"id":{"type":"string","description":"Auto-incrementing stream event identifier."}},"required":["event","data","id"]},{"type":"object","properties":{"event":{"type":"string","enum":["delta"]},"data":{"$ref":"#/components/schemas/SSEDeltaEvent"},"id":{"type":"string","description":"Auto-incrementing stream event identifier."}},"required":["event","data","id"]},{"type":"object","properties":{"event":{"type":"string","enum":["done"]},"data":{"$ref":"#/components/schemas/SSEDoneEvent"},"id":{"type":"string","description":"Auto-incrementing stream event identifier."}},"required":["event","data","id"]},{"type":"object","properties":{"event":{"type":"string","enum":["complete"]},"data":{"type":"object","properties":{"bean":{"$ref":"#/components/schemas/Bean"},"recipe":{"$ref":"#/components/schemas/Recipe"},"preferences":{"$ref":"#/components/schemas/Preferences"}},"required":["bean","recipe","preferences"]},"id":{"type":"string","description":"Auto-incrementing stream event identifier."}},"required":["event","data","id"]},{"type":"object","properties":{"event":{"type":"string","enum":["error"]},"data":{"$ref":"#/components/schemas/SSEErrorEvent"},"id":{"type":"string","description":"Auto-incrementing stream event identifier."}},"required":["event","data","id"]}]},"RecipeGenerationRequest":{"type":"object","properties":{"image":{"type":"string","format":"binary","description":"Coffee bean or packaging image file."},"machine":{"$ref":"#/components/schemas/RecipeMachineId"},"locale":{"$ref":"#/components/schemas/Locale"},"preferences":{"type":"string","description":"JSON-encoded Preferences object. See #/components/schemas/Preferences for structure.","default":"{}"}},"description":"Multipart form payload for generating a recipe from a coffee bean image."},"RecipeMachineId":{"type":"string","enum":["xbloomStudio"],"default":"xbloomStudio","description":"Target brewing machine identifier."},"ArtworkResponse":{"type":"object","properties":{"data":{"type":"object","properties":{"type":{"type":"string","enum":["artworks"],"description":"Resource type identifier."},"id":{"type":"string","description":"Unique resource identifier."},"attributes":{"$ref":"#/components/schemas/ArtworkData"}},"required":["type","id","attributes"],"description":"JSON:API resource object of type \"artworks\"."},"links":{"$ref":"#/components/schemas/Links"}},"required":["data"],"description":"JSON:API success response."},"ArtworkData":{"type":"object","properties":{"image":{"type":"string","description":"Base64-encoded image data."}},"required":["image"],"description":"Resource attributes."},"ArtworkGenerationRequest":{"type":"object","properties":{"bean":{"$ref":"#/components/schemas/Bean"},"recipe":{"$ref":"#/components/schemas/Recipe"},"preferences":{"$ref":"#/components/schemas/Preferences"}},"required":["bean","recipe","preferences"],"description":"Request payload for generating an abstract artwork image from recipe data."}},"parameters":{}},"paths":{"/auth":{"post":{"tags":["Authentication"],"summary":"Create an Authentication","description":"Verify a Sign in with Apple identity token and issue a server session for user-level access.","operationId":"post_CreateAuthentication","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateAuthenticationRequest"}}}},"responses":{"201":{"description":"Authentication Created. The server issued a bearer session token for the authenticated user.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateAuthenticationResponse"}}}},"400":{"description":"Malformed Request. The request is missing required fields or contains invalid values.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Identity Not Verified. The Apple identity token failed verification.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"500":{"description":"Server Misconfigured. The server is missing the Apple auth configuration.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}},"delete":{"tags":["Authentication"],"summary":"Delete an Authentication","description":"Revoke the bearer session token for the current Sign in with Apple authentication.","operationId":"delete_DeleteAuthentication","parameters":[{"schema":{"type":"string","description":"Bearer session token issued by POST /auth."},"required":true,"description":"Bearer session token issued by POST /auth.","name":"Authorization","in":"header"}],"responses":{"200":{"description":"Authentication Revoked. The current bearer session token is no longer valid.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DeleteAuthenticationResponse"}}}},"401":{"description":"Request Not Authorized. The bearer session token is missing or invalid.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/v1/app-attest/challenge":{"post":{"tags":["App Attest"],"summary":"Create an App Attest Challenge","description":"Create a one-time challenge for App Attest key registration. The challenge expires after five minutes.","operationId":"post_RequestAppAttestChallenge","responses":{"201":{"description":"Challenge Issued. The server returns a fresh challenge nonce.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ChallengeResponse"}}}},"500":{"description":"Server Misconfigured. The server is missing the database binding required to persist challenges.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/v1/app-attest/register":{"post":{"tags":["App Attest"],"summary":"Register an App Attest Key","description":"Verify an App Attest key and store it for request authentication.","operationId":"post_RegisterAppAttestKey","parameters":[{"schema":{"type":"string","description":"The App Attest key identifier."},"required":true,"description":"The App Attest key identifier.","name":"App-Attest-Key-Id","in":"header"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RegisterAttestationRequest"}}}},"responses":{"201":{"description":"Key Registered. The device's public key is stored and ready for assertion verification.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RegisterAttestationResponse"}}}},"400":{"description":"Malformed Request. The request is missing required fields or contains invalid values.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Attestation Not Verified. The challenge was invalid or expired, or the attestation failed verification.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"500":{"description":"Server Misconfigured. The server is missing the App Attest configuration or database binding.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/v1/attestation/challenge":{"post":{"tags":["App Attest"],"summary":"Create an App Attest Challenge","deprecated":true,"description":"Create a one-time challenge for App Attest key registration. The challenge expires after five minutes.","operationId":"post_RequestChallenge","responses":{"201":{"description":"Challenge Issued. The server returns a fresh challenge nonce.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ChallengeResponse"}}}},"500":{"description":"Server Misconfigured. The server is missing the database binding required to persist challenges.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/v1/attestation":{"post":{"tags":["App Attest"],"summary":"Register an App Attest Key","deprecated":true,"description":"Verify an App Attest key and store it for request authentication.","operationId":"post_RegisterAttestation","parameters":[{"schema":{"type":"string","description":"The App Attest key identifier."},"required":true,"description":"The App Attest key identifier.","name":"App-Attest-Key-Id","in":"header"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RegisterAttestationRequest"}}}},"responses":{"201":{"description":"Key Registered. The device's public key is stored and ready for assertion verification.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RegisterAttestationResponse"}}}},"400":{"description":"Malformed Request. The request is missing required fields or contains invalid values.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Attestation Not Verified. The challenge was invalid or expired, or the attestation failed verification.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"500":{"description":"Server Misconfigured. The server is missing the App Attest configuration or database binding.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/v1/beans":{"post":{"tags":["Beans"],"summary":"Analyze Bean Profile from a Coffee Bag Image","description":"Analyze a coffee bag image and return the extracted bean profile.","operationId":"post_AnalyzeBeanProfile","parameters":[{"schema":{"type":"string","description":"The App Attest key identifier."},"required":true,"description":"The App Attest key identifier.","name":"App-Attest-Key-Id","in":"header"},{"schema":{"type":"string","description":"A Base64-encoded App Attest assertion."},"required":true,"description":"A Base64-encoded App Attest assertion.","name":"App-Attest-Assertion","in":"header"},{"schema":{"type":"string","description":"Bearer session token issued by POST /auth."},"required":true,"description":"Bearer session token issued by POST /auth.","name":"Authorization","in":"header"}],"requestBody":{"required":true,"content":{"multipart/form-data":{"schema":{"$ref":"#/components/schemas/AnalyzeBeanProfileRequest"},"encoding":{"image":{"contentType":"image/jpeg, image/png, image/webp"}}}}},"responses":{"200":{"description":"Bean Profile Analyzed. The response contains the extracted bean profile.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AnalyzeBeanProfileResponse"}}}},"400":{"description":"Malformed Request. The multipart payload is invalid or the image field is missing.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Request Not Authorized. The App Attest assertion or Apple Account session failed validation.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"413":{"description":"Payload Too Large. The uploaded image exceeds the allowed size.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"415":{"description":"Unsupported Media Type. The request body must be `multipart/form-data`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"500":{"description":"Server Misconfigured. A required binding or environment variable is missing.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"502":{"description":"Upstream Service Failed. The bean analysis provider returned an error.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/v1/agents/bloomy/extract":{"post":{"tags":["Bloomy"],"summary":"Extract Bean Data from a Coffee Bag Image","description":"Stream incremental Bean field deltas followed by the final Bean payload extracted from a base64-encoded coffee bag image. Optional varietal metadata is included when visible on the packaging.","operationId":"post_ExtractBeanData","parameters":[{"schema":{"type":"string","description":"The App Attest key identifier."},"required":true,"description":"The App Attest key identifier.","name":"App-Attest-Key-Id","in":"header"},{"schema":{"type":"string","description":"A Base64-encoded App Attest assertion."},"required":true,"description":"A Base64-encoded App Attest assertion.","name":"App-Attest-Assertion","in":"header"},{"schema":{"type":"string","description":"Bearer session token issued by POST /auth."},"required":true,"description":"Bearer session token issued by POST /auth.","name":"Authorization","in":"header"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/InlineImagePayload"}}}},"responses":{"200":{"description":"Bean Stream Started. Server-sent events deliver extracted bean deltas and the final bean payload.","content":{"text/event-stream":{"schema":{"$ref":"#/components/schemas/ExtractBeanDataSSEEvent"}}}},"400":{"description":"Malformed Request. The request is missing required fields or contains invalid values.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Request Not Authorized. The App Attest assertion failed validation.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"413":{"description":"Payload Too Large. The inline image exceeds the allowed size.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"415":{"description":"Unsupported Media Type. The request body must be JSON.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"500":{"description":"Server Misconfigured. A required binding or environment variable is missing.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/v1/agents/bloomy/refine":{"post":{"tags":["Bloomy"],"summary":"Refine Bean Data with Browser-Backed Research","description":"Stream refinement deltas, source citations, and the final refined Bean payload from a normalized Bean. Optional varieties are preserved unless stronger evidence supports a change.","operationId":"post_RefineBeanData","parameters":[{"schema":{"type":"string","description":"The App Attest key identifier."},"required":true,"description":"The App Attest key identifier.","name":"App-Attest-Key-Id","in":"header"},{"schema":{"type":"string","description":"A Base64-encoded App Attest assertion."},"required":true,"description":"A Base64-encoded App Attest assertion.","name":"App-Attest-Assertion","in":"header"},{"schema":{"type":"string","description":"Bearer session token issued by POST /auth."},"required":true,"description":"Bearer session token issued by POST /auth.","name":"Authorization","in":"header"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Bean"}}}},"responses":{"200":{"description":"Refinement Stream Started. Server-sent events deliver refinement deltas, source citations, and the final refined bean payload.","content":{"text/event-stream":{"schema":{"$ref":"#/components/schemas/RefineBeanDataSSEEvent"}}}},"400":{"description":"Malformed Request. The request is missing required fields or contains invalid values.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Request Not Authorized. The App Attest assertion failed validation.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"413":{"description":"Payload Too Large. The inline image exceeds the allowed size.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"415":{"description":"Unsupported Media Type. The request body must be JSON.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"500":{"description":"Server Misconfigured. A required binding or environment variable is missing.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/v1/agents/bloomy/extract-and-refine":{"post":{"tags":["Bloomy"],"summary":"Extract and Refine Bean Data from a Coffee Bag Image","description":"Stream extraction deltas, refinement deltas, source citations, and the final combined Bean payload from a base64-encoded coffee bag image. Optional varietal metadata is included when visible on the packaging.","operationId":"post_ExtractAndRefineBeanData","parameters":[{"schema":{"type":"string","description":"The App Attest key identifier."},"required":true,"description":"The App Attest key identifier.","name":"App-Attest-Key-Id","in":"header"},{"schema":{"type":"string","description":"A Base64-encoded App Attest assertion."},"required":true,"description":"A Base64-encoded App Attest assertion.","name":"App-Attest-Assertion","in":"header"},{"schema":{"type":"string","description":"Bearer session token issued by POST /auth."},"required":true,"description":"Bearer session token issued by POST /auth.","name":"Authorization","in":"header"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/InlineImagePayload"}}}},"responses":{"200":{"description":"Combined Stream Started. Server-sent events deliver extraction deltas, refinement deltas, and the final combined bean payload.","content":{"text/event-stream":{"schema":{"$ref":"#/components/schemas/ExtractAndRefineBeanSSEEvent"}}}},"400":{"description":"Malformed Request. The request is missing required fields or contains invalid values.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Request Not Authorized. The App Attest assertion failed validation.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"413":{"description":"Payload Too Large. The inline image exceeds the allowed size.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"415":{"description":"Unsupported Media Type. The request body must be JSON.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"500":{"description":"Server Misconfigured. A required binding or environment variable is missing.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/v1/visual-intelligence/recipes":{"post":{"tags":["Visual Intelligence"],"summary":"Generate a Pour-Over Recipe from a Coffee Bag Image","description":"Analyze a coffee bag image, derive the bean profile (including optional varietal metadata when visible), and return a tailored pour-over recipe. The response format follows the `Accept` header: `application/json` returns the complete result when processing finishes; `text/event-stream` returns a Server-Sent Events stream with progress updates and the final result.","operationId":"post_GenerateRecipe","parameters":[{"schema":{"type":"string","description":"The App Attest key identifier."},"required":true,"description":"The App Attest key identifier.","name":"App-Attest-Key-Id","in":"header"},{"schema":{"type":"string","description":"A Base64-encoded App Attest assertion."},"required":true,"description":"A Base64-encoded App Attest assertion.","name":"App-Attest-Assertion","in":"header"},{"schema":{"type":"string","description":"Bearer session token issued by POST /auth."},"required":true,"description":"Bearer session token issued by POST /auth.","name":"Authorization","in":"header"}],"requestBody":{"required":true,"content":{"multipart/form-data":{"schema":{"$ref":"#/components/schemas/RecipeGenerationRequest"},"encoding":{"image":{"contentType":"image/jpeg"}}}}},"responses":{"200":{"description":"Recipe Generated. The response format matches the `Accept` header: a complete JSON envelope, or a Server-Sent Events stream of `status`, `delta`, `done`, `complete`, and `error` events.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RecipeResponse"}},"text/event-stream":{"schema":{"$ref":"#/components/schemas/RecipeSSEEvent"}}}},"400":{"description":"Malformed Request. The request is missing required fields or contains invalid values.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Request Not Authorized. The App Attest assertion failed validation.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"413":{"description":"Payload Too Large. The uploaded image exceeds the allowed size.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"415":{"description":"Unsupported Media Type. The request body must be `multipart/form-data`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"500":{"description":"Server Misconfigured. A required binding or environment variable is missing.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"502":{"description":"Upstream Service Failed. The recipe generation provider returned an error.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/v1/visual-intelligence/artworks":{"post":{"tags":["Visual Intelligence"],"summary":"Generate an Abstract Artwork Image for a Recipe","description":"Render an abstract painterly artwork inspired by a coffee recipe's roast level, processing method, tasting notes, and other characteristics. The result is suitable as a thumbnail, cover, or art print.","operationId":"post_GenerateArtwork","parameters":[{"schema":{"type":"string","description":"The App Attest key identifier."},"required":true,"description":"The App Attest key identifier.","name":"App-Attest-Key-Id","in":"header"},{"schema":{"type":"string","description":"A Base64-encoded App Attest assertion."},"required":true,"description":"A Base64-encoded App Attest assertion.","name":"App-Attest-Assertion","in":"header"},{"schema":{"type":"string","description":"Bearer session token issued by POST /auth."},"required":true,"description":"Bearer session token issued by POST /auth.","name":"Authorization","in":"header"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ArtworkGenerationRequest"}}}},"responses":{"200":{"description":"Artwork Generated. The response contains the generated artwork image payload.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ArtworkResponse"}}}},"400":{"description":"Malformed Request. The request is missing required fields or contains invalid values.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Request Not Authorized. The App Attest assertion failed validation.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"415":{"description":"Unsupported Media Type. The request body must be JSON.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"500":{"description":"Server Misconfigured. A required binding or environment variable is missing.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"502":{"description":"Upstream Service Failed. The image generation provider returned an error.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}}},"webhooks":{}}