{"openapi":"3.1.0","info":{"title":"Demarker API","description":"Identify what an AI image carries, then clean it. One credit is one GPU clean at 12 MP or less (two above that at original size). QUICK is free on the web and not sold per image. Endpoints return the price table with every identify, so clients never compute prices. Create an API key at /app/settings/api.","version":"1.0.0","x-errors-reference":"/api/v1/errors"},"servers":[{"url":"/api/v1","description":"This deployment"}],"security":[{"bearerAuth":[]}],"tags":[{"name":"Upload","description":"Presigned image upload"},{"name":"Jobs","description":"Identify, process, fetch and delete"},{"name":"Account","description":"Balance and prices"},{"name":"Webhooks","description":"Event notifications"},{"name":"Scan","description":"Keyless identify, no API key"}],"paths":{"/upload":{"post":{"operationId":"createUpload","summary":"Get a presigned upload URL","description":"Returns a URL valid for 15 minutes. PUT the image with the returned Content-Type, then call /identify with the uploadKey. A key works once and only for the account that requested it.","tags":["Upload"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["filename"],"properties":{"filename":{"type":"string"},"contentType":{"type":"string"}}}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"uploadUrl":{"type":"string"},"uploadKey":{"type":"string"},"expiresAt":{"type":"string","format":"date-time"},"maxFileSizeBytes":{"type":"integer"},"contentType":{"type":"string"}}}}}},"default":{"$ref":"#/components/responses/Error"}}}},"/identify":{"post":{"operationId":"identifyImage","summary":"Identify an uploaded image (free)","description":"Creates the job and runs the free identify step. Waits up to 5 seconds. If identify is slower, responds 202 with the job id; poll GET /jobs/{id}. A refused image returns status REFUSED with refusalReason. Send an Idempotency-Key header to make retries safe.","tags":["Jobs"],"parameters":[{"name":"Idempotency-Key","in":"header","required":false,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["uploadKey"],"properties":{"uploadKey":{"type":"string"}}}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Job"}}}},"202":{"description":"Identify is still running","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string"},"status":{"type":"string"}}}}}},"default":{"$ref":"#/components/responses/Error"}}}},"/jobs":{"get":{"operationId":"listJobs","summary":"List jobs","description":"Newest first.","tags":["Jobs"],"parameters":[{"name":"status","in":"query","schema":{"type":"string"}},{"name":"limit","in":"query","schema":{"type":"integer","maximum":100}},{"name":"offset","in":"query","schema":{"type":"integer"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"jobs":{"type":"array","items":{"$ref":"#/components/schemas/Job"}},"total":{"type":"integer"}}}}}},"default":{"$ref":"#/components/responses/Error"}}},"post":{"operationId":"createJob","summary":"One-shot identify and process (not available yet)","description":"Returns 501. Use /identify then /jobs/{id}/process.","tags":["Jobs"],"responses":{"501":{"$ref":"#/components/responses/Error"},"default":{"$ref":"#/components/responses/Error"}}}},"/jobs/{id}":{"get":{"operationId":"getJob","summary":"Get a job","description":"Status, report, offers and, when completed, a URL for the result valid for one hour (outputs.image).","tags":["Jobs"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Job"}}}},"default":{"$ref":"#/components/responses/Error"}}},"delete":{"operationId":"deleteJob","summary":"Delete a job now","description":"Deletes the original and the result at once. Credits are refunded while the job is PENDING. A running job returns 409.","tags":["Jobs"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"deleted":{"type":"boolean"},"creditsRefunded":{"type":"integer"}}}}}},"default":{"$ref":"#/components/responses/Error"}}}},"/jobs/{id}/process":{"post":{"operationId":"processJob","summary":"Process an identified job","description":"Debits the offer for the chosen tier. A short balance returns 402 with requiredCredits, balance and purchaseUrl; the job stays IDENTIFIED. Images over 25 MP cannot be processed. rightsAttested must be true.","tags":["Jobs"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["tier","rightsAttested"],"properties":{"tier":{"type":"string","enum":["QUICK","GPU"]},"keepResolution":{"type":"boolean","description":"Keep original size above 12 MP. Priced at the large band."},"vendorHint":{"type":"string","description":"Where the image was made, for the GPU tier when identify found no hidden target."},"force":{"type":"boolean"},"marks":{"type":"array","items":{"type":"string"},"description":"Registry keys to remove. Defaults to all allowed."},"secondAttestation":{"type":"string"},"rightsAttested":{"type":"boolean"}}}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Job"}}}},"402":{"$ref":"#/components/responses/Error"},"409":{"$ref":"#/components/responses/Error"},"422":{"$ref":"#/components/responses/Error"},"451":{"$ref":"#/components/responses/Error"},"default":{"$ref":"#/components/responses/Error"}}}},"/balance":{"get":{"operationId":"getBalance","summary":"Get credit balance","description":"","tags":["Account"],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"balanceCredits":{"type":"integer"},"updatedAt":{"type":"string","format":"date-time"}}}}}},"default":{"$ref":"#/components/responses/Error"}}}},"/pricing":{"get":{"operationId":"getPricing","summary":"Get the price table","description":"Public. Credits per tier and band, and the credit packs.","tags":["Account"],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object"}}}},"default":{"$ref":"#/components/responses/Error"}},"security":[]}},"/webhooks":{"get":{"operationId":"listWebhooks","summary":"List webhooks","description":"","tags":["Webhooks"],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object"}}}},"default":{"$ref":"#/components/responses/Error"}}},"post":{"operationId":"createWebhook","summary":"Create a webhook","description":"https URLs only. The signing secret is shown once.","tags":["Webhooks"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["url"],"properties":{"url":{"type":"string","format":"uri"},"events":{"type":"array","items":{"type":"string","enum":["job.identified","job.refused","job.process_blocked","job.completed","job.failed"]}}}}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object"}}}},"201":{"description":"Created"},"default":{"$ref":"#/components/responses/Error"}}}},"/webhooks/{id}":{"delete":{"operationId":"deleteWebhook","summary":"Delete a webhook","description":"","tags":["Webhooks"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"deleted":{"type":"boolean"}}}}}},"default":{"$ref":"#/components/responses/Error"}}}},"/errors":{"get":{"operationId":"listErrors","summary":"Error catalog","description":"Every error code with its resolution. Public.","tags":["Account"],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object"}}}},"default":{"$ref":"#/components/responses/Error"}},"security":[]}},"/scan":{"post":{"operationId":"scanImage","summary":"Keyless scan: upload and identify (free)","description":"No API key needed. Send the image as multipart/form-data in the field 'file'. Starts the free identify step and returns 202 with the job id and a claimToken. Poll GET /scan/{id} with the token in X-Claim-Token. Shares the web upload limits (per-IP rate limit, region block, size cap). Scan jobs can only be identified: to clean an image, upload it with /upload and use an API key and credits. Originals are deleted after 24 hours.","tags":["Scan"],"security":[],"requestBody":{"required":true,"content":{"multipart/form-data":{"schema":{"type":"object","required":["file"],"properties":{"file":{"type":"string","format":"binary"}}}}}},"responses":{"202":{"description":"Accepted","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string"},"claimToken":{"type":"string","description":"Secret. Send as X-Claim-Token when polling."},"status":{"type":"string"},"width":{"type":"integer"},"height":{"type":"integer"},"megapixels":{"type":"number"},"identifyOnly":{"type":"boolean"}}}}}},"default":{"$ref":"#/components/responses/Error"}}}},"/scan/{id}":{"get":{"operationId":"getScan","summary":"Get a keyless scan","description":"Returns the job once identify has run (status IDENTIFIED or REFUSED). The X-Claim-Token header is required; a wrong token and an unknown id both return 404.","tags":["Scan"],"security":[],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}},{"name":"X-Claim-Token","in":"header","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Job"}}}},"default":{"$ref":"#/components/responses/Error"}}}}},"components":{"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","description":"Bearer sk_live_... key. Create one at /app/settings/api (Settings, API) while signed in; it is shown once."}},"responses":{"Error":{"description":"Error","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"},"resolution":{"type":"string"},"docs":{"type":"string"}}}}}}}}},"schemas":{"Job":{"type":"object","properties":{"id":{"type":"string"},"status":{"type":"string","enum":["UPLOADED","IDENTIFYING","IDENTIFIED","HELD","PENDING","PROCESSING","COMPLETED","FAILED","REFUSED","EXPIRED"]},"width":{"type":"integer"},"height":{"type":"integer"},"megapixels":{"type":"number"},"hasInvisibleTarget":{"type":["boolean","null"]},"report":{"type":["object","null"],"description":"Engine provenance report, schema_version 1."},"cmiFields":{"description":"Author and copyright fields found. They are kept."},"offers":{"type":["array","null"],"items":{"type":"object","properties":{"tier":{"type":"string"},"keepResolution":{"type":"boolean"},"credits":{"type":"integer"}}}},"balance":{"type":"integer"},"refusalReason":{"type":["string","null"],"enum":["NSFW","THIRD_PARTY_OVERLAY",null]},"creditsCharged":{"type":"integer"},"errorCode":{"type":["string","null"]},"outputs":{"type":"object","properties":{"image":{"type":["string","null"],"format":"uri"}}},"createdAt":{"type":"string","format":"date-time"}}}}}}