{"openapi":"3.1.0","info":{"title":"Needle fine-tuning API","version":"1","description":"Generate tool-calling training data, fine-tune Needle 3, and download the resulting models. Read /llms.txt first for the flow and the rules for agents."},"servers":[{"url":"https://cactuscompute.com/v1"}],"security":[{"apiKey":[]}],"components":{"securitySchemes":{"apiKey":{"type":"http","scheme":"bearer","description":"A key from the console API Keys page, `needle_ft_…`. Keys cannot mint keys or change billing; those need a website login."}},"schemas":{"Error":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["message","type","code"],"properties":{"message":{"type":"string"},"type":{"const":"platform_error"},"code":{"type":"string","enum":["unauthorized","session_required","cross_origin_request","subscription_required","quota_exceeded","insufficient_storage","file_in_use","file_not_found","not_found","invalid_upload","invalid_input","invalid_request","invalid_expiry","starter_unavailable","invalid_payment","invalid_plan","job_active","queue_limit_reached","output_incomplete","storage_not_deleted","conflicting_customer","conflicting_subscription","subscription_exists","invalid_subscription","invalid_webhook","rate_limited","request_too_large","request_timeout","unsupported_operation","billing_disabled","billing_misconfigured","checkout_unavailable","base_model_unavailable","platform_unavailable","release_unavailable","storage_unavailable","download_unavailable","drafting_unavailable","drafting_failed","internal_error"]},"param":{"type":"string","description":"The request field at fault, dotted path"},"url":{"type":"string","format":"uri","description":"Console page that clears this error; hand it to your human"}}}}},"File":{"type":"object","required":["id","object","filename","bytes","created_at","purpose"],"properties":{"id":{"type":"string","pattern":"^file-"},"object":{"const":"file"},"filename":{"type":"string"},"bytes":{"type":"integer"},"created_at":{"type":"integer","description":"Unix seconds"},"purpose":{"const":"fine-tune"}}},"Upload":{"type":"object","required":["id","url","expires_at"],"properties":{"id":{"type":"string","description":"Upload id (bare uuid); the file id after `complete` is `file-<this>`"},"url":{"type":"string","format":"uri","description":"PUT the raw bytes here with Content-Type application/octet-stream"},"expires_at":{"type":"string","format":"date-time"}}},"JobFile":{"type":"object","required":["id","filename","group","ordinal"],"properties":{"id":{"type":"string"},"filename":{"type":"string"},"group":{"type":"string","enum":["train","validation","test","unknown"]},"ordinal":{"type":"integer"}}},"Job":{"type":"object","required":["id","object","status","created_at"],"properties":{"id":{"type":"string","pattern":"^ftjob-"},"object":{"type":"string","enum":["fine_tuning.job","generation"]},"name":{"oneOf":[{"type":"string"},{"type":"null"}]},"deleted":{"type":"boolean"},"status":{"type":"string","enum":["queued","running","succeeded","failed","cancelled"]},"created_at":{"type":"integer","description":"Unix seconds"},"started_at":{"oneOf":[{"type":"integer","description":"Unix seconds"},{"type":"null"}]},"finished_at":{"oneOf":[{"type":"integer","description":"Unix seconds"},{"type":"null"}]},"elapsed_seconds":{"oneOf":[{"type":"integer"},{"type":"null"}]},"cancellation_requested":{"type":"boolean"},"error":{"oneOf":[{"type":"object","properties":{"code":{"type":"string","enum":["invalid_input","execution_failed","deadline_exceeded","subscription_required","insufficient_storage","output_incomplete"]}}},{"type":"null"}]},"model":{"const":"needle-3","description":"Fine-tunes only"},"max_depth":{"type":"integer","description":"Fine-tunes only"},"completed_steps":{"type":"integer","description":"Fine-tunes only, 0 to 5"},"total_steps":{"type":"integer","description":"Fine-tunes only, always 5"},"evaluations":{"type":"array","description":"Fine-tunes only; empty until status is succeeded, then one entry per depth from 2 to max_depth","items":{"type":"object","required":["depth","validation","test"],"properties":{"depth":{"type":"integer"},"validation":{"type":"object","required":["correct","total","excluded"],"properties":{"correct":{"type":"integer"},"total":{"type":"integer"},"excluded":{"type":"integer","description":"Examples with no tools defined, left out of the score"}}},"test":{"type":"object","required":["correct","total","excluded"],"properties":{"correct":{"type":"integer"},"total":{"type":"integer"},"excluded":{"type":"integer","description":"Examples with no tools defined, left out of the score"}}}}}},"fine_tuned_model":{"oneOf":[{"type":"string","pattern":"^model-","description":"Fine-tunes only; the full-depth model once succeeded"},{"type":"null"}]},"files":{"type":"array","items":{"$ref":"#/components/schemas/JobFile"},"description":"On retrieve only. Fine-tunes: the submitted inputs. Generations: the files it published (empty until succeeded)"}}},"Model":{"type":"object","required":["id","object","owned_by","depth","variants"],"properties":{"id":{"type":"string"},"object":{"const":"model"},"owned_by":{"type":"string"},"name":{"oneOf":[{"type":"string"},{"type":"null"}]},"depth":{"type":"integer","description":"Layers"},"bytes":{"type":"integer"},"created_at":{"type":"integer","description":"Unix seconds"},"job_id":{"type":"string"},"variants":{"type":"array","description":"Every size this model was exported at; `is_full` marks the one equal to max_depth","items":{"type":"object","required":["id","depth","is_full"],"properties":{"id":{"type":"string"},"depth":{"type":"integer"},"is_full":{"type":"boolean"},"bytes":{"type":"integer"}}}}}},"Limits":{"type":"object","properties":{"jobs":{"type":"integer","description":"Fine-tunes per access period"},"examples":{"type":"integer","description":"Generated examples per access period"},"storage_bytes":{"type":"integer","description":"Standing storage limit"}}},"Plan":{"type":"object","required":["id","name","price_usd","billing_type","duration_days","once_per_account","price_usd_per_month","limits"],"properties":{"id":{"type":"string"},"object":{"const":"plan"},"name":{"type":"string"},"price_usd":{"oneOf":[{"type":"number"},{"type":"null"}]},"billing_type":{"type":"string","enum":["free","one_time","monthly","custom"]},"duration_days":{"oneOf":[{"type":"integer"},{"type":"null"}]},"once_per_account":{"type":"boolean"},"price_usd_per_month":{"oneOf":[{"type":"number","description":"Monthly plans only; null for one-time purchases"},{"type":"null"}]},"limits":{"oneOf":[{"$ref":"#/components/schemas/Limits"},{"type":"null"}]},"execution_mode":{"type":"string","enum":["local","hosted"]},"features":{"type":"array","items":{"type":"string"}},"get_started_url":{"type":"string","format":"uri","description":"Self-serve instructions for the Free local workflow; no hosted entitlement"},"contact_url":{"type":"string","format":"uri"}}},"Billing":{"type":"object","required":["enabled","access_state","plan","limits","usage"],"properties":{"enabled":{"type":"boolean"},"access_state":{"type":"string","enum":["inactive","active","grace","deleting"]},"plan":{"oneOf":[{"type":"string"},{"type":"null"}]},"limits":{"oneOf":[{"$ref":"#/components/schemas/Limits"},{"type":"null"}]},"usage":{"$ref":"#/components/schemas/Limits"},"period_start":{"oneOf":[{"type":"integer","description":"Unix seconds"},{"type":"null"}]},"period_end":{"oneOf":[{"type":"integer","description":"Unix seconds"},{"type":"null"}]},"delete_not_before":{"oneOf":[{"type":"string"},{"type":"null"}]},"starter":{"type":"object","properties":{"eligible":{"type":"boolean"},"paid_at":{"oneOf":[{"type":"string","format":"date-time"},{"type":"null"}]},"expires_at":{"oneOf":[{"type":"string","format":"date-time"},{"type":"null"}]},"upgraded_at":{"oneOf":[{"type":"string","format":"date-time"},{"type":"null"}]}}},"plans":{"type":"array","items":{"type":"object"}}}},"ApiKey":{"type":"object","required":["id","name","key_suffix","created_at"],"properties":{"id":{"type":"string","format":"uuid"},"name":{"type":"string"},"key_suffix":{"type":"string"},"created_at":{"type":"string","format":"date-time"},"expires_at":{"oneOf":[{"type":"string","format":"date-time"},{"type":"null"}]},"revoked_at":{"oneOf":[{"type":"string","format":"date-time"},{"type":"null"}]},"secret":{"type":"string","description":"Only on creation, shown once"}}},"ToolSchema":{"type":"object","required":["object","tools"],"properties":{"object":{"const":"tool_schema"},"tools":{"type":"array","items":{"type":"object"},"description":"OpenAI-form function definitions, 3 to 5 of them"}}}}},"paths":{"/plans":{"get":{"summary":"List plans and limits","security":[],"responses":{"200":{"description":"Plans","content":{"application/json":{"schema":{"type":"object","required":["object","data","has_more"],"properties":{"object":{"const":"list"},"data":{"type":"array","items":{"$ref":"#/components/schemas/Plan"}},"has_more":{"type":"boolean"},"first_id":{"type":"string"},"last_id":{"type":"string"}}}}}},"4XX":{"description":"The request was rejected; `code` says why and `url` (when present) is the console page that clears it","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/billing":{"get":{"summary":"Your plan, limits and usage this period","responses":{"200":{"description":"Billing","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Billing"}}}},"4XX":{"description":"The request was rejected; `code` says why and `url` (when present) is the console page that clears it","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/tool_schemas":{"post":{"summary":"Draft tool definitions from a product description (15 per day)","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"description":{"type":"string","minLength":10,"maxLength":1000}},"required":["description"],"additionalProperties":false}}}},"responses":{"200":{"description":"Drafted tools","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ToolSchema"}}}},"4XX":{"description":"The request was rejected; `code` says why and `url` (when present) is the console page that clears it","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/files":{"get":{"summary":"List files, uploaded and generated","parameters":[{"name":"limit","in":"query","schema":{"type":"integer","minimum":1,"maximum":100,"default":20}},{"name":"after","in":"query","schema":{"type":"string"},"description":"Cursor: the last id of the previous page"},{"name":"search","in":"query","schema":{"type":"string","maxLength":255}}],"responses":{"200":{"description":"Files","content":{"application/json":{"schema":{"type":"object","required":["object","data","has_more"],"properties":{"object":{"const":"list"},"data":{"type":"array","items":{"$ref":"#/components/schemas/File"}},"has_more":{"type":"boolean"},"first_id":{"type":"string"},"last_id":{"type":"string"}}}}}},"4XX":{"description":"The request was rejected; `code` says why and `url` (when present) is the console page that clears it","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"post":{"summary":"Reserve an upload (step 1 of 3)","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string","maxLength":255,"pattern":"^[^\\x00-\\x1f\\x7f/\\\\]+\\.jsonl$"},"bytes":{"type":"integer","minimum":1,"maximum":536870912}},"required":["name","bytes"],"additionalProperties":false}}}},"responses":{"201":{"description":"Reservation","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Upload"}}}},"4XX":{"description":"The request was rejected; `code` says why and `url` (when present) is the console page that clears it","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/files/{upload_id}/complete":{"post":{"summary":"Register the uploaded bytes as a file (step 3 of 3)","parameters":[{"name":"upload_id","in":"path","required":true,"schema":{"type":"string","pattern":"^[0-9a-f-]{36}$"}}],"responses":{"201":{"description":"File","content":{"application/json":{"schema":{"$ref":"#/components/schemas/File"}}}},"4XX":{"description":"The request was rejected; `code` says why and `url` (when present) is the console page that clears it","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/files/{upload_id}/abandon":{"post":{"summary":"Release a reservation you will not complete","parameters":[{"name":"upload_id","in":"path","required":true,"schema":{"type":"string","pattern":"^[0-9a-f-]{36}$"}}],"responses":{"200":{"description":"Released","content":{"application/json":{"schema":{"type":"object"}}}},"4XX":{"description":"The request was rejected; `code` says why and `url` (when present) is the console page that clears it","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/files/{file_id}":{"get":{"summary":"Retrieve a file","parameters":[{"name":"file_id","in":"path","required":true,"schema":{"type":"string","pattern":"^file-"}}],"responses":{"200":{"description":"File","content":{"application/json":{"schema":{"$ref":"#/components/schemas/File"}}}},"4XX":{"description":"The request was rejected; `code` says why and `url` (when present) is the console page that clears it","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"delete":{"summary":"Delete a file","parameters":[{"name":"file_id","in":"path","required":true,"schema":{"type":"string","pattern":"^file-"}}],"responses":{"200":{"description":"Deleted","content":{"application/json":{"schema":{"type":"object"}}}},"4XX":{"description":"The request was rejected; `code` says why and `url` (when present) is the console page that clears it","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/files/{file_id}/content":{"get":{"summary":"Download a file (307 to a signed link valid 60 seconds)","parameters":[{"name":"file_id","in":"path","required":true,"schema":{"type":"string","pattern":"^file-"}}],"responses":{"307":{"description":"Redirect to the download"},"4XX":{"description":"The request was rejected; `code` says why and `url` (when present) is the console page that clears it","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/generations":{"get":{"summary":"List generation jobs","parameters":[{"name":"limit","in":"query","schema":{"type":"integer","minimum":1,"maximum":100,"default":20}},{"name":"after","in":"query","schema":{"type":"string"},"description":"Cursor: the last id of the previous page"}],"responses":{"200":{"description":"Jobs","content":{"application/json":{"schema":{"type":"object","required":["object","data","has_more"],"properties":{"object":{"const":"list"},"data":{"type":"array","items":{"$ref":"#/components/schemas/Job"}},"has_more":{"type":"boolean"},"first_id":{"type":"string"},"last_id":{"type":"string"}}}}}},"4XX":{"description":"The request was rejected; `code` says why and `url` (when present) is the console page that clears it","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"post":{"summary":"Generate training, validation and test files from tool definitions","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"tools":{"type":"array","items":{"type":"object","properties":{"type":{"type":"string","enum":["function"]},"function":{"type":"object","properties":{"name":{"type":"string","minLength":1,"maxLength":64},"description":{"type":"string","maxLength":4096},"parameters":{"type":"object","additionalProperties":{}}},"required":["name"],"additionalProperties":true}},"required":["type","function"],"additionalProperties":true},"minItems":1,"maxItems":100},"examples":{"type":"integer","minimum":100,"maximum":10000},"description":{"type":"string","maxLength":1000},"messages":{"type":"array","items":{"type":"string","maxLength":200},"maxItems":20},"suffix":{"type":"string","minLength":1,"maxLength":100,"pattern":"^[^\\x00-\\x1f\\x7f-\\x9f]+$"}},"required":["tools","examples"],"additionalProperties":false}}}},"responses":{"201":{"description":"Job","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Job"}}}},"4XX":{"description":"The request was rejected; `code` says why and `url` (when present) is the console page that clears it","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/generations/{job_id}":{"get":{"summary":"Retrieve a generation job and, once succeeded, its files","parameters":[{"name":"job_id","in":"path","required":true,"schema":{"type":"string","pattern":"^ftjob-"}}],"responses":{"200":{"description":"Job","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Job"}}}},"4XX":{"description":"The request was rejected; `code` says why and `url` (when present) is the console page that clears it","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"delete":{"summary":"Delete a finished generation job","parameters":[{"name":"job_id","in":"path","required":true,"schema":{"type":"string","pattern":"^ftjob-"}}],"responses":{"200":{"description":"Deleted","content":{"application/json":{"schema":{"type":"object"}}}},"4XX":{"description":"The request was rejected; `code` says why and `url` (when present) is the console page that clears it","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/generations/{job_id}/cancel":{"post":{"summary":"Cancel a generation job","parameters":[{"name":"job_id","in":"path","required":true,"schema":{"type":"string","pattern":"^ftjob-"}}],"responses":{"200":{"description":"Job","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Job"}}}},"4XX":{"description":"The request was rejected; `code` says why and `url` (when present) is the console page that clears it","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/fine_tuning/jobs":{"get":{"summary":"List fine-tunes","parameters":[{"name":"limit","in":"query","schema":{"type":"integer","minimum":1,"maximum":100,"default":20}},{"name":"after","in":"query","schema":{"type":"string"},"description":"Cursor: the last id of the previous page"}],"responses":{"200":{"description":"Jobs","content":{"application/json":{"schema":{"type":"object","required":["object","data","has_more"],"properties":{"object":{"const":"list"},"data":{"type":"array","items":{"$ref":"#/components/schemas/Job"}},"has_more":{"type":"boolean"},"first_id":{"type":"string"},"last_id":{"type":"string"}}}}}},"4XX":{"description":"The request was rejected; `code` says why and `url` (when present) is the console page that clears it","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"post":{"summary":"Start a fine-tune (spends one run of your allowance)","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"model":{"type":"string","enum":["needle-3"]},"training_files":{"type":"array","items":{"type":"string","pattern":"^file-[0-9a-f-]{36}$"},"minItems":1,"maxItems":100},"validation_files":{"type":"array","items":{"type":"string","pattern":"^file-[0-9a-f-]{36}$"},"minItems":1,"maxItems":100},"test_files":{"type":"array","items":{"type":"string","pattern":"^file-[0-9a-f-]{36}$"},"minItems":1,"maxItems":100},"max_depth":{"type":"integer","minimum":2,"maximum":64},"suffix":{"type":"string","minLength":1,"maxLength":100,"pattern":"^[^\\x00-\\x1f\\x7f-\\x9f]+$"}},"required":["model","training_files","validation_files","test_files","max_depth"],"additionalProperties":false}}}},"responses":{"201":{"description":"Job","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Job"}}}},"4XX":{"description":"The request was rejected; `code` says why and `url` (when present) is the console page that clears it","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/fine_tuning/jobs/{job_id}":{"get":{"summary":"Retrieve a fine-tune: status, steps, evaluations per depth, inputs","parameters":[{"name":"job_id","in":"path","required":true,"schema":{"type":"string","pattern":"^ftjob-"}}],"responses":{"200":{"description":"Job","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Job"}}}},"4XX":{"description":"The request was rejected; `code` says why and `url` (when present) is the console page that clears it","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"delete":{"summary":"Delete a finished fine-tune","parameters":[{"name":"job_id","in":"path","required":true,"schema":{"type":"string","pattern":"^ftjob-"}}],"responses":{"200":{"description":"Deleted","content":{"application/json":{"schema":{"type":"object"}}}},"4XX":{"description":"The request was rejected; `code` says why and `url` (when present) is the console page that clears it","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/fine_tuning/jobs/{job_id}/cancel":{"post":{"summary":"Cancel a fine-tune","parameters":[{"name":"job_id","in":"path","required":true,"schema":{"type":"string","pattern":"^ftjob-"}}],"responses":{"200":{"description":"Job","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Job"}}}},"4XX":{"description":"The request was rejected; `code` says why and `url` (when present) is the console page that clears it","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/models":{"get":{"summary":"List models; the base model needle-3 comes first","parameters":[{"name":"limit","in":"query","schema":{"type":"integer","minimum":1,"maximum":100,"default":20}},{"name":"after","in":"query","schema":{"type":"string"},"description":"Cursor: the last id of the previous page"},{"name":"search","in":"query","schema":{"type":"string","maxLength":255}}],"responses":{"200":{"description":"Models","content":{"application/json":{"schema":{"type":"object","required":["object","data","has_more"],"properties":{"object":{"const":"list"},"data":{"type":"array","items":{"$ref":"#/components/schemas/Model"}},"has_more":{"type":"boolean"},"first_id":{"type":"string"},"last_id":{"type":"string"}}}}}},"4XX":{"description":"The request was rejected; `code` says why and `url` (when present) is the console page that clears it","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/models/{model_id}":{"get":{"summary":"Retrieve a model and its sizes (needle-3, needle-3-depth-N, or model-…)","parameters":[{"name":"model_id","in":"path","required":true,"schema":{"type":"string","pattern":"^(needle-3|needle-3-depth-[0-9]+|model-)"}}],"responses":{"200":{"description":"Model","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Model"}}}},"4XX":{"description":"The request was rejected; `code` says why and `url` (when present) is the console page that clears it","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"delete":{"summary":"Delete a fine-tuned model and all its sizes","parameters":[{"name":"model_id","in":"path","required":true,"schema":{"type":"string","pattern":"^model-"}}],"responses":{"200":{"description":"Deleted","content":{"application/json":{"schema":{"type":"object"}}}},"4XX":{"description":"The request was rejected; `code` says why and `url` (when present) is the console page that clears it","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/models/{model_id}/content":{"get":{"summary":"Download the .cact file (307 to a link valid 60 seconds)","parameters":[{"name":"model_id","in":"path","required":true,"schema":{"type":"string","pattern":"^(needle-3|needle-3-depth-[0-9]+|model-)"}}],"responses":{"307":{"description":"Redirect to the download"},"4XX":{"description":"The request was rejected; `code` says why and `url` (when present) is the console page that clears it","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api_keys":{"get":{"summary":"List your keys (no secrets)","parameters":[{"name":"limit","in":"query","schema":{"type":"integer","minimum":1,"maximum":100,"default":20}},{"name":"after","in":"query","schema":{"type":"string"},"description":"Cursor: the last id of the previous page"}],"responses":{"200":{"description":"Keys","content":{"application/json":{"schema":{"type":"object","required":["object","data","has_more"],"properties":{"object":{"const":"list"},"data":{"type":"array","items":{"$ref":"#/components/schemas/ApiKey"}},"has_more":{"type":"boolean"},"first_id":{"type":"string"},"last_id":{"type":"string"}}}}}},"4XX":{"description":"The request was rejected; `code` says why and `url` (when present) is the console page that clears it","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"post":{"summary":"Create a key (website login only; an API key cannot mint keys)","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string","minLength":1,"maxLength":100,"pattern":"^[^\\x00-\\x1f\\x7f-\\x9f]+$"},"expires_at":{"type":"string","format":"date-time"}},"required":["name"],"additionalProperties":false}}}},"responses":{"201":{"description":"Key with its secret","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiKey"}}}},"4XX":{"description":"The request was rejected; `code` says why and `url` (when present) is the console page that clears it","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api_keys/{key_id}":{"delete":{"summary":"Revoke a key","parameters":[{"name":"key_id","in":"path","required":true,"schema":{"type":"string","pattern":"^[0-9a-f-]{36}$"}}],"responses":{"200":{"description":"Revoked","content":{"application/json":{"schema":{"type":"object"}}}},"4XX":{"description":"The request was rejected; `code` says why and `url` (when present) is the console page that clears it","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}}}}