{"openapi":"3.1.0","info":{"title":"iXiun Paste","version":"1.0.0","description":"A paste bin for paste.ixiun.com. Create a paste, share the link, and the reader sees the text. Everything here is described from the code that serves it."},"servers":[{"url":"https://paste.ixiun.com"}],"paths":{"/api/pastes":{"post":{"summary":"Create a paste","description":"Sends a JSON object describing the paste and gets back its short link. A paste can be protected with a password, set to burn after one read, or given a custom slug.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["content"],"properties":{"content":{"type":"string","description":"The text of the paste. Must be non-empty and at most 1048576 bytes (1 MiB by default). Stored and returned byte for byte: nothing is trimmed or changed."},"slug":{"type":"string","description":"A custom address, 3 to 64 characters of letters, digits and hyphens. Case does not matter; it is stored and returned in lower case. Left out, a random 7-character slug is made. These addresses are reserved and refused: api, raw, docs, assets, static, new, about.","pattern":"^[a-zA-Z0-9-]{3,64}$"},"expires_in":{"type":"integer","minimum":1,"maximum":31536000,"description":"Seconds from now before the paste expires, a whole number from 1 to 31536000 (one year), or null for never. Default 2592000 (30 days). An expired paste is gone the moment it expires.","default":2592000},"password":{"type":"string","description":"A non-empty password. Left out or null, the paste is open. The reader must send it in the X-Paste-Password header. It is never stored; the content is encrypted at rest."},"burn":{"type":"boolean","description":"true destroys the paste the first time it is successfully read. Default false.","default":false},"language":{"type":"string","description":"A hint for syntax highlighting: 1 to 32 characters of lower-case letters, digits, +, #, . and -. Default plaintext.","default":"plaintext"}}}}}},"responses":{"201":{"description":"The paste was created.","content":{"application/json":{"schema":{"type":"object","required":["slug","url","expires_at","burn","has_password","language"],"properties":{"slug":{"type":"string","description":"The paste's address on this site."},"url":{"type":"string","description":"The full link to the paste, built from base_url."},"expires_at":{"type":"string","format":"date-time","nullable":true,"description":"When the paste expires, UTC as YYYY-MM-DDTHH:MM:SSZ, or null for never."},"burn":{"type":"boolean","description":"Whether the paste burns after one read."},"has_password":{"type":"boolean","description":"Whether the paste is password-protected."},"language":{"type":"string","description":"The syntax-highlighting hint."}}}}}},"400":{"description":"The request body is not a single JSON object.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","description":"The error code."},"message":{"type":"string","description":"A message written for a person."}},"example":{"code":"invalid_json","message":"The request body is not a single JSON object."}}}}}}},"409":{"description":"That address is already in use by a live paste. The error also carries \"field\", naming the field.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","description":"The error code."},"message":{"type":"string","description":"A message written for a person."}},"example":{"code":"slug_taken","message":"That address is already in use by a live paste."}}}}}}},"413":{"description":"The content is over the maximum allowed size. The error also carries \"field\", naming the field.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","description":"The error code."},"message":{"type":"string","description":"A message written for a person."}},"example":{"code":"too_large","message":"The content is over the maximum allowed size."}}}}}}},"429":{"description":"This client has created too many pastes recently. The Retry-After header says how long to wait. The error also carries \"field\", naming the field.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","description":"The error code."},"message":{"type":"string","description":"A message written for a person."}},"example":{"code":"rate_limited","message":"This client has created too many pastes recently. The Retry-After header says how long to wait."}}}}}}}}}},"/api/pastes/{slug}":{"get":{"summary":"Read a paste","description":"Reads a paste by its slug. If the paste has a password, the password must be sent in the X-Paste-Password header; it is never read from the query string. Warning: reading a burn paste destroys it the first time it is successfully read. If ten people ask at the same moment, one gets the content and nine get 404.","parameters":[{"name":"slug","in":"path","required":true,"description":"The paste's slug. Lower case; case is not significant.","schema":{"type":"string"}},{"name":"X-Paste-Password","in":"header","required":false,"description":"The password when the paste is protected. Never a query parameter. A wrong or missing password never burns the paste.","schema":{"type":"string"}}],"responses":{"200":{"description":"The paste was read. For a burn paste, this read is the one that destroys it.","content":{"application/json":{"schema":{"type":"object","required":["slug","content","language","created_at","expires_at","burn","has_password"],"properties":{"slug":{"type":"string","description":"The paste's slug."},"content":{"type":"string","description":"The paste's text, byte for byte as it was created."},"language":{"type":"string","description":"The syntax-highlighting hint."},"created_at":{"type":"string","format":"date-time","description":"When the paste was created, UTC as YYYY-MM-DDTHH:MM:SSZ."},"expires_at":{"type":"string","format":"date-time","nullable":true,"description":"When the paste expires, UTC, or null for never."},"burn":{"type":"boolean","description":"Whether the paste burns after one read."},"has_password":{"type":"boolean","description":"Whether the paste is password-protected."}}}}}},"401":{"description":"The paste has a password and none was sent.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","description":"The error code."},"message":{"type":"string","description":"A message written for a person."}},"example":{"code":"password_required","message":"The paste has a password and none was sent."}}}}}}},"404":{"description":"No such paste: it never existed, or has already expired or been read. The error also carries \"field\", naming the field.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","description":"The error code."},"message":{"type":"string","description":"A message written for a person."}},"example":{"code":"not_found","message":"No such paste: it never existed, or has already expired or been read."}}}}}}},"429":{"description":"This client has sent too many wrong passwords for this paste recently. The Retry-After header says how long to wait. The error also carries \"field\", naming the field.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","description":"The error code."},"message":{"type":"string","description":"A message written for a person."}},"example":{"code":"rate_limited","message":"This client has sent too many wrong passwords for this paste recently. The Retry-After header says how long to wait."}}}}}}}}}}},"components":{"schemas":{"Error":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","description":"A stable machine-readable code."},"message":{"type":"string","description":"A message written for a person."},"field":{"type":"string","description":"The field that broke its rules, only on a 400 invalid_field."}}}}},"errorCodes":{"type":"array","description":"Every error code the API can return: invalid_json, invalid_field, password_required, password_incorrect, not_found, method_not_allowed, slug_taken, too_large, rate_limited","items":{"type":"string"}}}},"x-limits":{"create":{"limit":20,"window_seconds":3600,"description":"Once a client has created this many pastes inside the window, further creates from that client are 429 until the window moves on. Other clients are unaffected. Limits are kept in the database."},"password":{"limit":10,"window_seconds":900,"description":"Once a client has sent this many wrong passwords for one paste inside the window, every further attempt by that client on that paste is 429, even with the right password, until the window moves on. Other clients and other pastes are unaffected. A request with no password at all is not an attempt."},"retry_after":"A 429 answer carries a Retry-After header in whole seconds."}}