{"openapi":"3.1.0","info":{"title":"FARPY Public API","version":"1.0.0","description":"Blender Cycles rendering at one USD cent per frame."},"servers":[{"url":"https://farpy.com/v1"}],"components":{"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"FARPY API key"},"sessionCookie":{"type":"apiKey","in":"cookie","name":"farpy_session"}},"schemas":{"Health":{"type":"object","required":["ok","service","ts"],"properties":{"ok":{"type":"boolean","const":true},"service":{"type":"string"},"ts":{"type":"string","format":"date-time"}}},"Pricing":{"type":"object","required":["ok","contract","currency","model","price_per_frame_cents"],"properties":{"ok":{"type":"boolean","const":true},"contract":{"type":"string","const":"FARPY-PUBLIC-API-V1"},"currency":{"type":"string","const":"USD"},"model":{"type":"string","const":"per_frame"},"price_per_frame_cents":{"type":"integer","const":1},"minimum_charge_cents":{"type":"integer","const":1}}},"Capabilities":{"type":"object","properties":{"ok":{"type":"boolean","const":true},"contract":{"type":"string","const":"FARPY-PUBLIC-API-V1"},"renderers":{"type":"array","items":{"type":"object"}},"workflow":{"type":"array","items":{"type":"string"}},"proof":{"type":"object"}}},"Limits":{"type":"object","properties":{"ok":{"type":"boolean","const":true},"contract":{"type":"string","const":"FARPY-PUBLIC-API-V1"},"frame_count":{"type":"object","properties":{"minimum":{"type":"integer","const":1},"maximum":{"type":"integer","const":10000}}},"accepted_extensions":{"type":"array","items":{"type":"string","const":".blend"}},"required_render_engine":{"type":"string","const":"CYCLES"},"maximum_upload_size_bytes":{"type":"integer","const":104857600},"rate_limit":{"type":"object","required":["requests_per_minute_per_client","burst"],"properties":{"requests_per_minute_per_client":{"type":"integer","const":60},"burst":{"type":"integer","const":20}}}}},"ApiKeyMetadata":{"type":"object","required":["key_id","name","masked_prefix","created_at","last_used_at","revoked_at"],"properties":{"key_id":{"type":"string","pattern":"^key_[a-f0-9]{32}$"},"name":{"type":"string","minLength":1,"maxLength":80},"masked_prefix":{"type":"string"},"created_at":{"type":"string","format":"date-time"},"last_used_at":{"type":["string","null"],"format":"date-time"},"revoked_at":{"type":["string","null"],"format":"date-time"}}},"ApiKeyList":{"type":"object","required":["ok","keys"],"properties":{"ok":{"type":"boolean","const":true},"keys":{"type":"array","items":{"$ref":"#/components/schemas/ApiKeyMetadata"}}}},"ApiKeyCreateRequest":{"type":"object","required":["name"],"properties":{"name":{"type":"string","minLength":1,"maxLength":80}}},"ApiKeyCreated":{"type":"object","required":["ok","key","key_id","name","created_at"],"properties":{"ok":{"type":"boolean","const":true},"key":{"type":"string","pattern":"^farpy_"},"key_id":{"type":"string","pattern":"^key_[a-f0-9]{32}$"},"name":{"type":"string"},"created_at":{"type":"string","format":"date-time"}}},"ApiKeyRevoked":{"type":"object","required":["ok","key_id","revoked"],"properties":{"ok":{"type":"boolean","const":true},"key_id":{"type":"string","pattern":"^key_[a-f0-9]{32}$"},"revoked":{"type":"boolean","const":true}}},"RenderObject":{"type":"object","additionalProperties":true,"properties":{"webhook_configured":{"type":"boolean","description":"True when terminal render webhooks are configured."}}},"RenderUploadRequest":{"type":"object","required":["file"],"properties":{"file":{"type":"string","format":"binary"},"webhook_url":{"type":"string","format":"uri","maxLength":2048,"description":"HTTPS endpoint for render.completed, render.failed, and render.cancelled events."},"webhook_secret":{"type":"string","minLength":16,"writeOnly":true,"description":"Optional signing secret. FARPY generates one when omitted."}}},"RenderProof":{"type":"object","required":["ok","job_id","proof","receipt_url"],"properties":{"ok":{"type":"boolean","const":true},"job_id":{"type":"string"},"proof":{"type":"string","const":"receipt_backed"},"receipt_url":{"type":"string"}}},"WorkspaceRenders":{"type":"object","required":["ok","renders"],"properties":{"ok":{"type":"boolean","const":true},"renders":{"type":"array","items":{"$ref":"#/components/schemas/RenderObject"}}}},"PreflightRequest":{"type":"object","required":["filename"],"properties":{"filename":{"type":"string","pattern":"\\.blend$"},"renderer":{"type":"string","const":"blender","default":"blender"},"frame_start":{"type":"integer","minimum":1,"default":1},"frame_end":{"type":"integer","minimum":1},"frame_count":{"type":"integer","minimum":1,"maximum":10000}}},"Error":{"type":"object","required":["ok","error"],"properties":{"ok":{"type":"boolean","const":false},"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string"},"message":{"type":"string"},"detail":{}}}}}}},"paths":{"/health":{"get":{"responses":{"200":{"description":"Service health","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Health"}}}}}}},"/capabilities":{"get":{"responses":{"200":{"description":"Supported render capabilities","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Capabilities"}}}}}}},"/pricing":{"get":{"responses":{"200":{"description":"Current pricing","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Pricing"}}}}}}},"/limits":{"get":{"responses":{"200":{"description":"Current limits","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Limits"}}}}}}},"/renders/preflight":{"post":{"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PreflightRequest"}}}},"responses":{"200":{"description":"Eligible render request"},"400":{"description":"Invalid request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/renders":{"post":{"security":[{"bearerAuth":[]}],"requestBody":{"required":true,"content":{"multipart/form-data":{"schema":{"$ref":"#/components/schemas/RenderUploadRequest"}}}},"responses":{"200":{"description":"Render job created","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RenderObject"}}}},"401":{"description":"Invalid API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"415":{"description":"Multipart form data required","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/renders/{job_id}/submit":{"post":{"security":[{"bearerAuth":[]}],"parameters":[{"name":"job_id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Render submitted","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RenderObject"}}}},"401":{"description":"Invalid API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/renders/{job_id}/cancel":{"post":{"security":[{"bearerAuth":[]}],"parameters":[{"name":"job_id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Render cancelled"},"404":{"description":"Render not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Render cannot be cancelled","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit exceeded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/renders/{job_id}":{"get":{"security":[{"bearerAuth":[]}],"parameters":[{"name":"job_id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Render status","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RenderObject"}}}},"401":{"description":"Invalid API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/renders/{job_id}/download":{"get":{"security":[{"bearerAuth":[]}],"parameters":[{"name":"job_id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Download URL","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RenderObject"}}}},"401":{"description":"Invalid API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/renders/{job_id}/receipt":{"get":{"security":[{"bearerAuth":[]}],"parameters":[{"name":"job_id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Receipt URL","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RenderObject"}}}},"401":{"description":"Invalid API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/renders/{job_id}/proof":{"get":{"security":[{"bearerAuth":[]}],"parameters":[{"name":"job_id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Receipt-backed proof","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RenderProof"}}}},"401":{"description":"Invalid API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/workspace/renders":{"get":{"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Workspace render history","content":{"application/json":{"schema":{"$ref":"#/components/schemas/WorkspaceRenders"}}}},"401":{"description":"Invalid API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api-keys":{"get":{"security":[{"sessionCookie":[]}],"responses":{"200":{"description":"API key metadata","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiKeyList"}}}},"401":{"description":"Not authenticated","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"post":{"security":[{"sessionCookie":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiKeyCreateRequest"}}}},"responses":{"201":{"description":"API key created","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiKeyCreated"}}}},"400":{"description":"Invalid key name","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Not authenticated","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Active key limit reached","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api-keys/{key_id}":{"delete":{"security":[{"sessionCookie":[]}],"parameters":[{"name":"key_id","in":"path","required":true,"schema":{"type":"string","pattern":"^key_[a-f0-9]{32}$"}}],"responses":{"200":{"description":"API key revoked","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiKeyRevoked"}}}},"401":{"description":"Not authenticated","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"API key not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}}}}