{
  "openapi": "3.1.0",
  "info": {
    "title": "MegaPotato AI Image Upscaler API",
    "version": "1.0",
    "summary": "Image upscaling (super-resolution) REST API, ×2 to ×8: #1 of 18 AI upscalers in the October 2026 blind benchmark and the cheapest of the leaders. Submit a photo, poll the job, download the result.",
    "description": "AI image upscaling (photo super-resolution) over REST: upload an image, get it back ×2, ×4 or ×8\nlarger with detail restored, or refined at the same size, and download the PNG. In the October 2026 blind benchmark of 18 AI upscalers (open data, CC BY 4.0) the\nmodel came first and is the cheapest of the leaders: https://megapotato.app/blog/best-ai-image-upscalers-2026.\n\n**Asynchronous by design.** On a warm start a 1 MP photo is done in about 3 s at ×1, 15 s at ×4\nand 45 s at ×8. The first\njob after a quiet period is a cold start: a GPU has to come up first, which adds 2–3 minutes.\nSo the API never holds a request for the whole run: you `POST /v1/upscale` to get a job id,\nthen `GET /v1/jobs/{job_id}` to poll it. The poll accepts `?wait=` (up to 55 s), so a warm job\ncompletes inside a single call.\n\n**Billing.** Every submit returns `cost_chips`: the amount held from the key owner's balance\nat submit. It is captured when the result is delivered and refunded automatically if the job\nfails or is cancelled — you only pay for results you receive.\n\n**Price first.** `POST /v1/quote` returns the price for given dimensions and factor without\nuploading or charging anything.\n\n**Errors.** Every `/v1` error — auth, validation, rate limit, server fault — uses one envelope:\n`{\"error\": {\"type\": ..., \"message\": ...}}`.\n\n**Daily spending limit.** Every account has a daily API spending limit (UTC day) so a leaked key cannot drain a balance; when it is reached, submits answer `429 spend_limit_reached` until 00:00 UTC (`Retry-After` = seconds to midnight UTC). The default is shown on the account page, where a lower limit can be set.\n\n**Keyless trial.** `POST /v1/try` (the same body, no header) runs one real job inside a small hourly budget\nand `GET /v1/try/{job_id}` polls it; nothing is charged. A `403` means the trial is closed, a `429` a budget.\nThe current budget is on https://megapotato.app/developers#try.\n\n**Test keys.** `mp_test_…` keys (created on the account page) validate and price uploads like\nlive keys but never charge or run. Their jobs, `test_<upscale>_<hex>`, always poll as\n`completed` with a sample result: the same sample in `result_url` and `result_jpeg_url`, and\n`jpeg_status` always `ready`, whether `jpeg=true` was sent or not.\n\n**Keys.** Create keys on your account page (https://megapotato.app/account#api): test keys (`mp_test_…`) right away; live keys (`mp_live_…`) for every signed-in account or after the first chip pack, whichever https://megapotato.app/developers#test-keys currently states (new accounts start with free chips). A key is shown once; keep it on your server and send it only in the `Authorization` header. Examples use `$MEGAPOTATO_API_KEY`.\n\nThe always-current documentation lives at https://megapotato.app/developers (Markdown: https://megapotato.app/developers.md). Prices per photo are set by the operator and quoted live by `POST /v1/quote`; the public price list is https://megapotato.app/pricing.\n",
    "contact": {
      "name": "MegaPotato",
      "url": "https://megapotato.app",
      "email": "support@megapotato.app"
    },
    "termsOfService": "https://megapotato.app/terms"
  },
  "servers": [
    {
      "url": "https://megapotato.app",
      "description": "Production"
    }
  ],
  "security": [
    {
      "bearerAuth": []
    }
  ],
  "tags": [
    {
      "name": "upscale",
      "description": "Submit and track image upscaling (super-resolution) jobs."
    }
  ],
  "paths": {
    "/v1/upscale": {
      "post": {
        "tags": [
          "upscale"
        ],
        "operationId": "submitUpscale",
        "summary": "Submit an image for upscaling",
        "description": "Uploads an image and queues a super-resolution job. Responds as soon as the job is\naccepted and the chips are held — it does not wait for the result.\n\n**Accepted input:** PNG, JPEG, WebP or HEIC; at most 40 MiB on the wire; at most 6000 px on the\nlong edge and 20 250 000 px total area.\n\n**Output ceiling:** the requested output (input dimensions x `upscale`) must stay within\n16 400 px per side and 268 960 000 px total. This is checked from the upload header before\nany pixel work, so an oversized request is rejected up front and never charged. In practice\nthat means `upscale=4` needs an input of 4100 px or less per side, and `upscale=8` needs\n2050 px or less; `upscale` of 1 or 2 accepts any admitted input.\n\n**Retries.** Always send `Idempotency-Key` on uploads — a slow request can time out on the\nclient after the job was already created — and reuse it when retrying: the same key returns\nthe same job and charges once. Reusing a key with a different image, `upscale` or `jpeg` is a 422.\n\n**JPEG.** A lossless 16K PNG can weigh several hundred MB. `jpeg=true` adds a full-size JPEG of the\nsame result (quality 92, no chroma subsampling), usually a small fraction of that, at no extra cost.\nThe job then carries `result_jpeg_url` and `jpeg_status`.\n",
        "parameters": [
          {
            "name": "Idempotency-Key",
            "in": "header",
            "required": false,
            "description": "Client-chosen retry token, 1-128 characters. Recommended on every upload. Repeating a submit with the same key returns the original job instead of creating (and charging) a second one.",
            "schema": {
              "type": "string",
              "minLength": 1,
              "maxLength": 128
            },
            "example": "order-1234-photo-7"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "multipart/form-data": {
              "schema": {
                "type": "object",
                "required": [
                  "file"
                ],
                "properties": {
                  "file": {
                    "type": "string",
                    "format": "binary",
                    "description": "The source image (PNG, JPEG, WebP or HEIC)."
                  },
                  "upscale": {
                    "type": "integer",
                    "enum": [
                      1,
                      2,
                      4,
                      8
                    ],
                    "default": 4,
                    "description": "Scale factor. 1 enhances without resizing; 2, 4 and 8 enlarge by that factor."
                  },
                  "jpeg": {
                    "type": "boolean",
                    "default": false,
                    "description": "`true` also delivers a full-size JPEG of the result (quality 92, no chroma subsampling) next to the PNG, at no extra cost: `result_jpeg_url` on the job. The PNG is delivered either way."
                  }
                }
              },
              "encoding": {
                "file": {
                  "contentType": "image/png, image/jpeg, image/webp, image/heic, image/heif"
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Job accepted and queued; chips held.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SubmitResponse"
                }
              }
            }
          },
          "400": {
            "description": "Undecodable image, or dimensions over the input/output limits.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "description": "Not enough chips on the key owner's balance; the message states available and needed.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "413": {
            "description": "Request body over the 40 MiB upload cap.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "415": {
            "description": "Unsupported image format.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "Invalid `upscale` or `jpeg`, missing `file`, malformed `Idempotency-Key`, or an idempotency key reused for a different image, `upscale` or `jpeg`; or `content_not_allowed`: the image doesn't meet our content rules (see the Terms). Nothing was charged.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          },
          "503": {
            "description": "`service_unavailable`: we can't accept images right now. Nothing was charged; retry after `Retry-After` seconds.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v1/try": {
      "post": {
        "tags": [
          "upscale"
        ],
        "operationId": "tryUpscale",
        "summary": "Run one upscale with no account and no key (budgeted trial)",
        "description": "The keyless trial: the same multipart body as `POST /v1/upscale`, no `Authorization` header, a REAL job on the\nsame model. `cost_chips` in the answer is what a key would pay; nothing is charged. Poll the job on\n`GET /v1/try/{job_id}` (the job id is the only credential). `Idempotency-Key` is ignored here. Trial jobs queue behind every keyed job, so they can wait longer when the service is busy.\n\n**Budgets.** A few jobs an hour per client address, a shared ceiling for everyone, and a size limit per job\n(the current numbers are on https://megapotato.app/developers#try). Over a budget: `429 rate_limit_error`\nwith `Retry-After` (seconds until a slot frees); a photo dearer than the size limit: `422` with the price and\nthe limit; while the operator has closed the trial: `403`. Every refusal says where a key comes from.\n",
        "security": [],
        "requestBody": {
          "required": true,
          "content": {
            "multipart/form-data": {
              "schema": {
                "type": "object",
                "required": [
                  "file"
                ],
                "properties": {
                  "file": {
                    "type": "string",
                    "format": "binary",
                    "description": "The source image (PNG, JPEG, WebP or HEIC)."
                  },
                  "upscale": {
                    "type": "integer",
                    "enum": [
                      1,
                      2,
                      4,
                      8
                    ],
                    "default": 4,
                    "description": "Scale factor. 1 enhances without resizing; 2, 4 and 8 enlarge by that factor."
                  },
                  "jpeg": {
                    "type": "boolean",
                    "default": false,
                    "description": "`true` also delivers a full-size JPEG of the result (quality 92, no chroma subsampling) next to the PNG, at no extra cost: `result_jpeg_url` on the job. The PNG is delivered either way."
                  }
                }
              },
              "encoding": {
                "file": {
                  "contentType": "image/png, image/jpeg, image/webp, image/heic, image/heif"
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Job accepted and queued; nothing charged.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SubmitResponse"
                }
              }
            }
          },
          "400": {
            "description": "Undecodable image, or dimensions over the input/output limits.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "The keyless trial is closed right now.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "413": {
            "description": "Request body over the 40 MiB upload cap.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "415": {
            "description": "Unsupported image format.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "Invalid `upscale`, missing `file`, or a photo dearer than the trial's size limit (the message states the price and the limit); or `content_not_allowed`: the image doesn't meet our content rules (see the Terms). Nothing was charged.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "A trial budget is used up (the message says whether the address's or the shared one), or the per-address request limit; `Retry-After` says when to retry.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          },
          "503": {
            "description": "`service_unavailable`: we can't accept images right now. Nothing was charged; retry after `Retry-After` seconds.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v1/try/{job_id}": {
      "get": {
        "tags": [
          "upscale"
        ],
        "operationId": "getTrialJob",
        "summary": "Get a trial job's status and result",
        "description": "The same as `GET /v1/jobs/{job_id}` (long-polling with `?wait=`, `result_url` on completion) for a job\ncreated by `POST /v1/try`, with no `Authorization` header: the job id is the only credential. Only trial jobs are\nvisible here (a keyed account's job id answers 404), and reading stays open while the trial is closed.\n",
        "security": [],
        "parameters": [
          {
            "name": "job_id",
            "in": "path",
            "required": true,
            "description": "The job id returned by `POST /v1/try` (a UUID).",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "wait",
            "in": "query",
            "required": false,
            "description": "Seconds to hold the request while waiting for a terminal state.",
            "schema": {
              "type": "integer",
              "minimum": 0,
              "maximum": 55,
              "default": 0
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The job's current state.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Job"
                }
              }
            }
          },
          "404": {
            "description": "No such trial job.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "`wait` outside 0-55.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        }
      }
    },
    "/v1/quote": {
      "post": {
        "tags": [
          "upscale"
        ],
        "operationId": "quoteUpscale",
        "summary": "Price a job without uploading",
        "description": "Returns the price and output size for the given input dimensions and factor. Free: nothing\nis uploaded, held or charged. Dimensions over the input or output limits answer\n`400 invalid_request_error`.\n",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "width",
                  "height",
                  "upscale"
                ],
                "properties": {
                  "width": {
                    "type": "integer",
                    "minimum": 1,
                    "description": "Input width in px."
                  },
                  "height": {
                    "type": "integer",
                    "minimum": 1,
                    "description": "Input height in px."
                  },
                  "upscale": {
                    "type": "integer",
                    "enum": [
                      1,
                      2,
                      4,
                      8
                    ]
                  }
                }
              },
              "example": {
                "width": 4032,
                "height": 3024,
                "upscale": 2
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The price for that job.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "cost_chips",
                    "output_width",
                    "output_height",
                    "upscale"
                  ],
                  "properties": {
                    "cost_chips": {
                      "type": "integer",
                      "description": "Chips a submit with these parameters would hold."
                    },
                    "output_width": {
                      "type": "integer"
                    },
                    "output_height": {
                      "type": "integer"
                    },
                    "upscale": {
                      "type": "integer",
                      "enum": [
                        1,
                        2,
                        4,
                        8
                      ]
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Dimensions over the input or output limits.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/v1/jobs/{job_id}": {
      "get": {
        "tags": [
          "upscale"
        ],
        "operationId": "getJob",
        "summary": "Get job status and result",
        "description": "Returns the current state of one of your jobs. Only jobs submitted with your own key are\nvisible: an unknown id, a malformed id and someone else's job all answer the same 404.\n\n**Long-polling.** With `?wait=N` (up to 55 s) the server holds the request until the job\nreaches a terminal state, then answers immediately; if the budget runs out first it answers\nwith the current state anyway. 55 s is the ceiling because proxied requests are cut at the\nnetwork edge at roughly 100 s.\n\n**Result URL.** `result_url` is `null` until the job completes, then carries a presigned\ndownload URL valid for about 1 hour. Fetch it with a plain GET and no `Authorization`\nheader. Poll the job again to mint a fresh URL; results stay retrievable for 90 days.\n\n**JPEG.** A job submitted with `jpeg=true` also carries `result_jpeg_url` (a presigned JPEG link\nunder the same rules as `result_url`) and `jpeg_status`: `pending` while the job is queued or running,\nand also on a `completed` job while the JPEG is still being uploaded — usually for seconds, in rare\ncases up to about an hour (the PNG in `result_url` already works; poll again for the JPEG), `ready`, or `unavailable` when the job completed without one (the PNG is still delivered), failed or\nexpired. Both are `null` for a job submitted without `jpeg=true`. A test key's job always carries the sample\nimage in `result_url` and `result_jpeg_url` with `jpeg_status` `ready`, whether `jpeg=true` was sent or not.\n",
        "parameters": [
          {
            "name": "job_id",
            "in": "path",
            "required": true,
            "description": "The job id returned by `POST /v1/upscale` (a UUID; jobs submitted with a test key use `test_<upscale>_<hex>`).",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "wait",
            "in": "query",
            "required": false,
            "description": "Seconds to hold the request while waiting for a terminal state.",
            "schema": {
              "type": "integer",
              "minimum": 0,
              "maximum": 55,
              "default": 0
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The job's current state.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Job"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "description": "No such job under this key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "`wait` outside 0-55.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        }
      }
    }
  },
  "components": {
    "securitySchemes": {
      "bearerAuth": {
        "type": "http",
        "scheme": "bearer",
        "description": "Your API key, sent as `Authorization: Bearer YOUR_API_KEY` (an `mp_live_...` or `mp_test_...` key) on every request. Keys are never accepted in query parameters, so they cannot leak into access logs or browser history."
      }
    },
    "schemas": {
      "SubmitResponse": {
        "type": "object",
        "required": [
          "id",
          "status",
          "upscale",
          "cost_chips"
        ],
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid",
            "description": "Job id — poll it on `/v1/jobs/{job_id}`."
          },
          "status": {
            "$ref": "#/components/schemas/JobStatus"
          },
          "upscale": {
            "type": "integer",
            "enum": [
              1,
              2,
              4,
              8
            ],
            "description": "The accepted scale factor."
          },
          "cost_chips": {
            "type": "integer",
            "description": "Chips held from the key owner's balance for this job. The amount follows the live price list; ask `POST /v1/quote` for it before uploading. On the keyless trial (`POST /v1/try`) it is the price a key would have paid: nothing is held or charged there."
          }
        }
      },
      "Job": {
        "type": "object",
        "required": [
          "id",
          "status",
          "result_url",
          "error",
          "upscale",
          "created_at"
        ],
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "status": {
            "$ref": "#/components/schemas/JobStatus"
          },
          "result_url": {
            "type": [
              "string",
              "null"
            ],
            "format": "uri",
            "description": "Presigned download URL, valid about 1 hour. `null` until the job completes and `null` again once the link expires — re-read the job to mint a fresh one."
          },
          "error": {
            "type": [
              "string",
              "null"
            ],
            "description": "Failure reason; `null` unless `status` is `failed`. When the hold was returned the message says so explicitly."
          },
          "upscale": {
            "type": "integer",
            "enum": [
              1,
              2,
              4,
              8
            ]
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "eta_seconds": {
            "type": [
              "number",
              "null"
            ],
            "description": "Estimated seconds until the result, from this job's size and queue position; `null` when unknown or terminal. An estimate, not a promise — keep polling."
          },
          "result_jpeg_url": {
            "type": [
              "string",
              "null"
            ],
            "format": "uri",
            "description": "Presigned download URL of the full-size JPEG asked for with `jpeg=true`, under the same rules as `result_url` (valid about 1 hour, `null` until completed and again once expired). `null` when not asked or not available. Test keys are the exception: the sample image, whether `jpeg=true` was sent or not."
          },
          "jpeg_status": {
            "type": [
              "string",
              "null"
            ],
            "enum": [
              "pending",
              "ready",
              "unavailable",
              null
            ],
            "description": "`null` when the job was submitted without `jpeg=true`. Otherwise `pending` while queued or running, and also on a `completed` job while the JPEG is still being uploaded — usually for seconds, in rare cases up to about an hour (`result_url` already works; poll again), `ready` when `result_jpeg_url` is set, `unavailable` when the job completed without a JPEG (the PNG is still delivered), failed or expired. Test keys are the exception: always `ready`, with the sample image in both URLs."
          }
        },
        "example": {
          "id": "690ff0b0-c118-4832-96f8-0c9eaf6b2e0a",
          "status": "completed",
          "result_url": "https://storage.example/presigned...",
          "error": null,
          "upscale": 4,
          "created_at": "2026-08-26T17:57:04Z",
          "eta_seconds": null,
          "result_jpeg_url": "https://storage.example/presigned...",
          "jpeg_status": "ready"
        }
      },
      "JobStatus": {
        "type": "string",
        "enum": [
          "queued",
          "running",
          "completed",
          "failed"
        ],
        "description": "Lifecycle: `queued` to `running` to `completed` or `failed`. A cancelled job reports `failed` with an explanatory `error`. Treat any unrecognised value as non-terminal and keep polling."
      },
      "Error": {
        "type": "object",
        "required": [
          "error"
        ],
        "properties": {
          "error": {
            "type": "object",
            "required": [
              "type",
              "message"
            ],
            "properties": {
              "type": {
                "type": "string",
                "enum": [
                  "invalid_request_error",
                  "authentication_error",
                  "insufficient_credits",
                  "rate_limit_error",
                  "spend_limit_reached",
                  "server_error",
                  "content_not_allowed",
                  "service_unavailable"
                ],
                "description": "Stable machine-readable class — branch on this, not on the message text."
              },
              "message": {
                "type": "string",
                "description": "Human-readable explanation. Wording may change; do not parse it."
              }
            }
          }
        },
        "example": {
          "error": {
            "type": "invalid_request_error",
            "message": "upscale must be one of 1, 2, 4, 8"
          }
        }
      }
    },
    "responses": {
      "Unauthorized": {
        "description": "Missing, malformed, unknown, revoked or expired key. All five answer identically so the API never confirms whether a given key exists.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      },
      "RateLimited": {
        "description": "Per-key rate limit exceeded, or 10 jobs already queued/running for the account (`rate_limit_error`; `Retry-After: 15` for the latter), or the daily API spending limit reached (`spend_limit_reached`, resets 00:00 UTC). Honour the `Retry-After` header.",
        "headers": {
          "Retry-After": {
            "description": "Seconds to wait before retrying; for `spend_limit_reached`, seconds to midnight UTC.",
            "schema": {
              "type": "integer"
            }
          }
        },
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      },
      "ServerError": {
        "description": "Server-side fault. Polls are always safe to retry; retry a submit only with your `Idempotency-Key` so it cannot be charged twice.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      }
    }
  },
  "externalDocs": {
    "description": "Developer documentation",
    "url": "https://megapotato.app/developers"
  }
}
