{
  "openapi": "3.0.3",
  "info": {
    "title": "Filemonk API",
    "version": "1.0.0",
    "description": "API for managing digital products, assets, orders, and downloads in Filemonk.\n\nAll endpoints require authentication via the `X-Auth-Token` header. You can\nfind your API key in the Filemonk app under **Settings > Other > API**.\n\n## Rate Limiting\n\nAll endpoints are rate-limited using a token-bucket algorithm:\n- **Bucket size**: 40 points per 60-second window\n- **Read requests** (GET): 1 point\n- **Write requests** (POST/PATCH/DELETE): 2 points\n\nRate limit status is returned in response headers:\n- `X-RateLimit-Limit` — Maximum points per window\n- `X-RateLimit-Remaining` — Points remaining in current window\n- `X-RateLimit-Reset` — Unix timestamp when the window resets\n\nWhen the rate limit is exceeded, a `429 Too Many Requests` response is returned\nwith a `Retry-After` header indicating how many seconds to wait.\n"
  },
  "servers": [
    {
      "url": "https://app.filemonk.io/api/v1/external",
      "description": "Production server"
    }
  ],
  "security": [
    {
      "ApiKeyAuth": []
    }
  ],
  "paths": {
    "/orders": {
      "get": {
        "summary": "List orders",
        "description": "Retrieve a paginated list of orders for the authenticated store.\nSupports searching by order name/number, filtering by customer email,\nfinancial status, and date range.\n",
        "tags": [
          "Orders"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/AuthToken"
          },
          {
            "in": "query",
            "name": "q",
            "schema": {
              "type": "string"
            },
            "description": "Search by order name/number or customer email (partial match)"
          },
          {
            "in": "query",
            "name": "email",
            "schema": {
              "type": "string"
            },
            "description": "Filter by customer email (partial match)"
          },
          {
            "in": "query",
            "name": "status",
            "schema": {
              "type": "string",
              "enum": [
                "paid",
                "pending",
                "partially_paid",
                "partially_refunded",
                "refunded",
                "voided",
                "authorized"
              ]
            },
            "description": "Filter by financial status"
          },
          {
            "in": "query",
            "name": "shopify_product_id",
            "schema": {
              "type": "integer",
              "format": "int64"
            },
            "description": "Only return orders that bought this Shopify product"
          },
          {
            "in": "query",
            "name": "digital_product_id",
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "Only return orders linked to this Filemonk digital product (collection UUID)"
          },
          {
            "in": "query",
            "name": "created_at_min",
            "schema": {
              "type": "string",
              "format": "date"
            },
            "description": "Filter orders created on or after this date (YYYY-MM-DD)"
          },
          {
            "in": "query",
            "name": "created_at_max",
            "schema": {
              "type": "string",
              "format": "date"
            },
            "description": "Filter orders created on or before this date (YYYY-MM-DD)"
          },
          {
            "in": "query",
            "name": "page",
            "schema": {
              "type": "integer",
              "default": 1,
              "minimum": 1
            },
            "description": "Page number"
          },
          {
            "in": "query",
            "name": "per_page",
            "schema": {
              "type": "integer",
              "default": 25,
              "minimum": 1,
              "maximum": 250
            },
            "description": "Number of orders per page (max 250)"
          },
          {
            "in": "query",
            "name": "sort",
            "schema": {
              "type": "string",
              "enum": [
                "created_at_desc",
                "created_at_asc",
                "name_asc",
                "name_desc"
              ],
              "default": "created_at_desc"
            },
            "description": "Sort order"
          }
        ],
        "responses": {
          "200": {
            "description": "A paginated list of orders",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "orders": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Order"
                      }
                    },
                    "pagination": {
                      "$ref": "#/components/schemas/Pagination"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "429": {
            "$ref": "#/components/responses/RateLimitExceeded"
          }
        }
      }
    },
    "/orders/{id}": {
      "get": {
        "summary": "Get an order",
        "description": "Retrieve a single order by its ID.",
        "tags": [
          "Orders"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/AuthToken"
          },
          {
            "in": "path",
            "name": "id",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "The order ID"
          }
        ],
        "responses": {
          "200": {
            "description": "The order",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "order": {
                      "$ref": "#/components/schemas/Order"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimitExceeded"
          }
        }
      }
    },
    "/orders/{id}/downloads": {
      "get": {
        "summary": "Get downloads for an order",
        "description": "Retrieve a flat list of all downloadable files for an order. Each item\nincludes pre-computed boolean flags indicating whether the download has\nexpired, the download limit has been reached, access has been revoked,\nor the file is part of a preorder that hasn't been released yet.\n\nThe `downloadable` field is a master flag — it is `true` only when all\nconditions are met (not expired, limit not reached, access not revoked,\npreorder released).\n\nWhen `downloadable` is `true`, the `download_url` field contains a\nsigned URL (for internal files) or the external URL. When `downloadable`\nis `false`, `download_url` is `null`.\n\nIf PDF watermarking is enabled for the shop, calling this endpoint also\nstarts watermark generation for eligible PDFs that do not have it yet.\nWatermarking is asynchronous: entries with `watermark_status` of\n`processing` have a `null` `download_url` and should be polled until the\nstatus becomes `ready`, at which point `download_url` points at the\nwatermarked copy of the file.\n",
        "tags": [
          "Downloads"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/AuthToken"
          },
          {
            "in": "path",
            "name": "id",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "The order ID"
          }
        ],
        "responses": {
          "200": {
            "description": "List of downloads for the order",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "order_id": {
                      "type": "string",
                      "format": "uuid"
                    },
                    "order_name": {
                      "type": "string",
                      "example": "#1001"
                    },
                    "downloads": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Download"
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimitExceeded"
          }
        }
      }
    },
    "/orders/{id}/attach_assets": {
      "post": {
        "summary": "Attach assets to order",
        "description": "Attach one or more existing assets to an order as custom files.\nCreates an order-level collection and links the assets to it.\nAssets attached this way appear on the customer's download page\nalongside product-level assets.\n\n**Beta** — this endpoint is in active development. The request and\nresponse shapes are stable for production use, but we may add new\nfields or capabilities without notice.\n",
        "tags": [
          "Orders"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/AuthToken"
          },
          {
            "in": "path",
            "name": "id",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "The order ID"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "asset_ids"
                ],
                "properties": {
                  "asset_ids": {
                    "type": "array",
                    "items": {
                      "type": "string",
                      "format": "uuid"
                    },
                    "description": "Array of asset UUIDs to attach to the order",
                    "example": [
                      "f1e2d3c4-b5a6-7890-fedc-ba0987654321"
                    ]
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Assets attached successfully",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "order": {
                      "$ref": "#/components/schemas/Order"
                    },
                    "attached_assets": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/AssetSummary"
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "422": {
            "$ref": "#/components/responses/UnprocessableEntity"
          },
          "429": {
            "$ref": "#/components/responses/RateLimitExceeded"
          }
        }
      }
    },
    "/orders/{id}/detach_assets": {
      "delete": {
        "summary": "Detach assets from order",
        "description": "Remove one or more custom-attached assets from an order. Only affects\nassets attached via the attach_assets endpoint — product-level assets\nlinked through digital products are not affected.\n\n**Beta** — this endpoint is in active development.\n",
        "tags": [
          "Orders"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/AuthToken"
          },
          {
            "in": "path",
            "name": "id",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "The order ID"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "asset_ids"
                ],
                "properties": {
                  "asset_ids": {
                    "type": "array",
                    "items": {
                      "type": "string",
                      "format": "uuid"
                    },
                    "description": "Array of asset UUIDs to detach from the order",
                    "example": [
                      "f1e2d3c4-b5a6-7890-fedc-ba0987654321"
                    ]
                  }
                }
              }
            }
          }
        },
        "responses": {
          "204": {
            "description": "Assets detached successfully"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "422": {
            "$ref": "#/components/responses/UnprocessableEntity"
          },
          "429": {
            "$ref": "#/components/responses/RateLimitExceeded"
          }
        }
      }
    },
    "/orders/{id}/notify": {
      "post": {
        "summary": "Notify order",
        "description": "Send (or re-send) the download notification email for an order. Uses\nthe email template configured in your Filemonk email settings.\n\n**Beta** — this endpoint is in active development.\n",
        "tags": [
          "Orders"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/AuthToken"
          },
          {
            "in": "path",
            "name": "id",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "The order ID"
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "email": {
                    "type": "string",
                    "format": "email",
                    "description": "Override the recipient email address. When omitted the email is sent to the customer email on file."
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Notification sent",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "order": {
                      "$ref": "#/components/schemas/Order"
                    },
                    "notified": {
                      "type": "boolean",
                      "description": "Whether the notification was successfully queued",
                      "example": true
                    },
                    "notified_at": {
                      "type": "string",
                      "format": "date-time",
                      "description": "When the notification was sent"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "422": {
            "$ref": "#/components/responses/UnprocessableEntity"
          },
          "429": {
            "$ref": "#/components/responses/RateLimitExceeded"
          }
        }
      }
    },
    "/assets": {
      "get": {
        "summary": "List assets",
        "description": "Retrieve a paginated list of all assets in your store. Assets are the\ndigital files and external links you deliver to customers. You can filter\nby type or search by name/filename.\n",
        "tags": [
          "Assets"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/AuthToken"
          },
          {
            "in": "query",
            "name": "q",
            "schema": {
              "type": "string"
            },
            "description": "Search by asset name or filename (partial match)"
          },
          {
            "in": "query",
            "name": "type",
            "schema": {
              "type": "string",
              "enum": [
                "internal",
                "external",
                "licence"
              ]
            },
            "description": "Filter by asset type"
          },
          {
            "in": "query",
            "name": "page",
            "schema": {
              "type": "integer",
              "default": 1,
              "minimum": 1
            },
            "description": "Page number"
          },
          {
            "in": "query",
            "name": "per_page",
            "schema": {
              "type": "integer",
              "default": 25,
              "minimum": 1,
              "maximum": 250
            },
            "description": "Number of assets per page (max 250)"
          }
        ],
        "responses": {
          "200": {
            "description": "A paginated list of assets",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "assets": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Asset"
                      }
                    },
                    "pagination": {
                      "$ref": "#/components/schemas/Pagination"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "429": {
            "$ref": "#/components/responses/RateLimitExceeded"
          }
        }
      },
      "post": {
        "summary": "Create an asset",
        "description": "Create a new asset. Supports three types:\n\n- `internal` — a file stored in Filemonk storage. The asset row is\n  created together with a single presigned `PUT` URL in one call.\n  Upload the file body to that URL to finish.\n- `external` — a file hosted at an external URL.\n- `licence` — a licence key asset.\n\nPlan storage / asset-count quotas are enforced before the upload URL\nis issued. The per-file ceiling is 2 GB.\n",
        "tags": [
          "Assets"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/AuthToken"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "asset"
                ],
                "properties": {
                  "asset": {
                    "type": "object",
                    "required": [
                      "type"
                    ],
                    "properties": {
                      "type": {
                        "type": "string",
                        "enum": [
                          "internal",
                          "external",
                          "licence"
                        ]
                      },
                      "name": {
                        "type": "string",
                        "description": "A display name for the asset. Required for `licence` type."
                      },
                      "external_url": {
                        "type": "string",
                        "format": "uri",
                        "description": "The URL of the external file. Required when `type` is `external`."
                      },
                      "filename": {
                        "type": "string",
                        "description": "Required when `type` is `internal`. Suggested attachment name on download."
                      },
                      "byte_size": {
                        "type": "integer",
                        "description": "Required when `type` is `internal`. Size of the file in bytes. Must be positive and at most 2 GB."
                      },
                      "content_type": {
                        "type": "string",
                        "description": "Optional MIME type for `internal` assets. Defaults to `application/octet-stream`."
                      },
                      "watermark_asset": {
                        "type": "boolean",
                        "default": false,
                        "description": "Whether to apply watermarking to this asset (PDFs only)."
                      },
                      "autogenerate_licence_key": {
                        "type": "boolean",
                        "default": false,
                        "description": "Whether to auto-generate a licence key when this asset is purchased. Only applicable to `licence` type."
                      }
                    }
                  }
                }
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Asset created. For `internal` assets the response includes an `upload` block describing how to upload the file body.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "asset": {
                      "$ref": "#/components/schemas/Asset"
                    },
                    "upload": {
                      "type": "object",
                      "description": "Only present when `type` is `internal`. Single-PUT upload handle.",
                      "properties": {
                        "method": {
                          "type": "string",
                          "enum": [
                            "PUT"
                          ]
                        },
                        "url": {
                          "type": "string",
                          "format": "uri"
                        },
                        "headers": {
                          "type": "object",
                          "additionalProperties": {
                            "type": "string"
                          }
                        },
                        "expires_at": {
                          "type": "string",
                          "format": "date-time"
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "422": {
            "$ref": "#/components/responses/UnprocessableEntity"
          },
          "429": {
            "$ref": "#/components/responses/RateLimitExceeded"
          }
        }
      }
    },
    "/assets/{id}": {
      "get": {
        "summary": "Get an asset",
        "description": "Retrieve a single asset by its ID.",
        "tags": [
          "Assets"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/AuthToken"
          },
          {
            "in": "path",
            "name": "id",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "The asset ID"
          }
        ],
        "responses": {
          "200": {
            "description": "The asset",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "asset": {
                      "$ref": "#/components/schemas/Asset"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimitExceeded"
          }
        }
      },
      "patch": {
        "summary": "Update an asset",
        "description": "Update the metadata of an existing asset.",
        "tags": [
          "Assets"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/AuthToken"
          },
          {
            "in": "path",
            "name": "id",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "The asset ID"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "asset"
                ],
                "properties": {
                  "asset": {
                    "type": "object",
                    "properties": {
                      "name": {
                        "type": "string",
                        "description": "Display name for the asset."
                      },
                      "external_url": {
                        "type": "string",
                        "format": "uri",
                        "description": "The URL of the external file. Only applicable to `external` type assets."
                      },
                      "watermark_asset": {
                        "type": "boolean",
                        "description": "Whether to apply watermarking to this asset."
                      },
                      "autogenerate_licence_key": {
                        "type": "boolean",
                        "description": "Whether to auto-generate a licence key on purchase."
                      }
                    }
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Asset updated",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "asset": {
                      "$ref": "#/components/schemas/Asset"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "422": {
            "$ref": "#/components/responses/UnprocessableEntity"
          },
          "429": {
            "$ref": "#/components/responses/RateLimitExceeded"
          }
        }
      },
      "delete": {
        "summary": "Delete an asset",
        "description": "Permanently delete an asset. This cannot be undone. If the asset is linked\nto any digital products, those links will also be removed.\n",
        "tags": [
          "Assets"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/AuthToken"
          },
          {
            "in": "path",
            "name": "id",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "The asset ID"
          }
        ],
        "responses": {
          "204": {
            "description": "Asset deleted"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimitExceeded"
          }
        }
      }
    },
    "/assets/{id}/replace": {
      "post": {
        "summary": "Replace an asset's file",
        "description": "Replace the file body of an existing `internal` asset in place. The asset\nID and all digital-product / order links are preserved, so you don't have\nto re-attach the new file — buyers downloading the asset receive the new\nbody automatically.\n\nLike creating an `internal` asset, the response includes a single\npresigned `PUT` URL. Upload the new file body to that URL to finish. The\nprevious file is removed once the replacement is committed, and any\nwatermarking is re-applied to the new body.\n\nOnly `internal` assets can be replaced. Plan storage quota is enforced on\nthe growth in size; the per-file ceiling is 2 GB.\n",
        "tags": [
          "Assets"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/AuthToken"
          },
          {
            "in": "path",
            "name": "id",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "The asset ID"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "asset"
                ],
                "properties": {
                  "asset": {
                    "type": "object",
                    "required": [
                      "filename",
                      "byte_size"
                    ],
                    "properties": {
                      "filename": {
                        "type": "string",
                        "description": "Suggested attachment name on download for the new file."
                      },
                      "byte_size": {
                        "type": "integer",
                        "description": "Size of the new file in bytes. Must be positive and at most 2 GB."
                      },
                      "content_type": {
                        "type": "string",
                        "description": "Optional MIME type for the new file. Defaults to the asset's current content type, or `application/octet-stream`."
                      }
                    }
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Replacement accepted. The response includes an `upload` block describing how to upload the new file body.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "asset": {
                      "$ref": "#/components/schemas/Asset"
                    },
                    "upload": {
                      "type": "object",
                      "description": "Single-PUT upload handle for the new file body.",
                      "properties": {
                        "method": {
                          "type": "string",
                          "enum": [
                            "PUT"
                          ]
                        },
                        "url": {
                          "type": "string",
                          "format": "uri"
                        },
                        "headers": {
                          "type": "object",
                          "additionalProperties": {
                            "type": "string"
                          }
                        },
                        "expires_at": {
                          "type": "string",
                          "format": "date-time"
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "422": {
            "$ref": "#/components/responses/UnprocessableEntity"
          },
          "429": {
            "$ref": "#/components/responses/RateLimitExceeded"
          }
        }
      }
    },
    "/digital_products": {
      "get": {
        "summary": "List digital products",
        "description": "Retrieve a paginated list of all digital products in your store. A digital\nproduct is a Shopify product or variant that has been linked to one or more\nassets in Filemonk. You can optionally filter by Shopify product ID.\n",
        "tags": [
          "Digital Products"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/AuthToken"
          },
          {
            "in": "query",
            "name": "shopify_product_id",
            "schema": {
              "type": "integer"
            },
            "description": "Filter by Shopify product ID (numeric)."
          },
          {
            "in": "query",
            "name": "page",
            "schema": {
              "type": "integer",
              "default": 1,
              "minimum": 1
            },
            "description": "Page number"
          },
          {
            "in": "query",
            "name": "per_page",
            "schema": {
              "type": "integer",
              "default": 25,
              "minimum": 1,
              "maximum": 100
            },
            "description": "Number of digital products per page (max 100)"
          }
        ],
        "responses": {
          "200": {
            "description": "A paginated list of digital products",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "digital_products": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/DigitalProduct"
                      }
                    },
                    "pagination": {
                      "$ref": "#/components/schemas/Pagination"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "429": {
            "$ref": "#/components/responses/RateLimitExceeded"
          }
        }
      },
      "post": {
        "summary": "Create a digital product",
        "description": "Link assets to a Shopify product or variant, creating a digital product.\nYou must provide a `shopify_product_id`. To link to a specific variant\n(rather than the product as a whole), also include `shopify_variant_id`.\nProvide an array of `asset_ids` to attach assets to this digital product.\n\n**Shopify IDs** are the numeric IDs (e.g. `8847261098265`), not GID strings.\nYou can find these in your Shopify admin URL or via the Shopify API.\n",
        "tags": [
          "Digital Products"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/AuthToken"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "digital_product"
                ],
                "properties": {
                  "digital_product": {
                    "type": "object",
                    "required": [
                      "shopify_product_id",
                      "asset_ids"
                    ],
                    "properties": {
                      "shopify_product_id": {
                        "type": "integer",
                        "description": "The numeric Shopify product ID.",
                        "example": 8847261098265
                      },
                      "shopify_variant_id": {
                        "type": "integer",
                        "nullable": true,
                        "description": "The numeric Shopify variant ID. Omit to link at the product level.",
                        "example": 46012345678921
                      },
                      "asset_ids": {
                        "type": "array",
                        "items": {
                          "type": "string",
                          "format": "uuid"
                        },
                        "description": "IDs of assets to attach to this digital product."
                      },
                      "upsert_always": {
                        "type": "boolean",
                        "default": false,
                        "description": "When `true`, a new copy of this digital product's assets is created for each order, even if the customer has ordered before."
                      },
                      "custom_message": {
                        "type": "string",
                        "description": "A custom message shown to customers on the download page."
                      },
                      "preorder_enabled": {
                        "type": "boolean",
                        "default": false,
                        "description": "Whether this digital product is a preorder. Requires `preorder_launch_date` and `custom_message`."
                      },
                      "preorder_launch_date": {
                        "type": "string",
                        "format": "date-time",
                        "description": "When the preorder content becomes available. Must be at least 2 hours in the future. Required when `preorder_enabled` is `true`."
                      },
                      "preorder_subject": {
                        "type": "string",
                        "description": "Email subject for the preorder notification."
                      },
                      "preorder_heading": {
                        "type": "string",
                        "description": "Heading shown on the download page while in preorder."
                      },
                      "preorder_description": {
                        "type": "string",
                        "description": "Description shown on the download page while in preorder."
                      }
                    }
                  }
                }
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Digital product created",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "digital_product": {
                      "$ref": "#/components/schemas/DigitalProduct"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "422": {
            "$ref": "#/components/responses/UnprocessableEntity"
          },
          "429": {
            "$ref": "#/components/responses/RateLimitExceeded"
          }
        }
      }
    },
    "/digital_products/{id}": {
      "get": {
        "summary": "Get a digital product",
        "description": "Retrieve a single digital product by its ID, including all linked assets.",
        "tags": [
          "Digital Products"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/AuthToken"
          },
          {
            "in": "path",
            "name": "id",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "The digital product ID"
          }
        ],
        "responses": {
          "200": {
            "description": "The digital product",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "digital_product": {
                      "$ref": "#/components/schemas/DigitalProduct"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimitExceeded"
          }
        }
      },
      "patch": {
        "summary": "Update a digital product",
        "description": "Update the settings or assets of an existing digital product. To replace the\nasset list, provide a new `asset_ids` array — this fully replaces all currently\nlinked assets. Omit `asset_ids` to leave the asset list unchanged.\n",
        "tags": [
          "Digital Products"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/AuthToken"
          },
          {
            "in": "path",
            "name": "id",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "The digital product ID"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "digital_product"
                ],
                "properties": {
                  "digital_product": {
                    "type": "object",
                    "properties": {
                      "asset_ids": {
                        "type": "array",
                        "items": {
                          "type": "string",
                          "format": "uuid"
                        },
                        "description": "Replacement list of asset IDs. Fully replaces the current asset list."
                      },
                      "upsert_always": {
                        "type": "boolean",
                        "description": "Whether to create a new copy of assets for each order."
                      },
                      "custom_message": {
                        "type": "string",
                        "description": "A custom message shown on the download page."
                      },
                      "preorder_enabled": {
                        "type": "boolean",
                        "description": "Whether this digital product is a preorder."
                      },
                      "preorder_launch_date": {
                        "type": "string",
                        "format": "date-time",
                        "description": "When preorder content becomes available. Required when `preorder_enabled` is `true`."
                      },
                      "preorder_subject": {
                        "type": "string",
                        "description": "Email subject for the preorder notification."
                      },
                      "preorder_heading": {
                        "type": "string",
                        "description": "Heading shown while in preorder."
                      },
                      "preorder_description": {
                        "type": "string",
                        "description": "Description shown while in preorder."
                      }
                    }
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Digital product updated",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "digital_product": {
                      "$ref": "#/components/schemas/DigitalProduct"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "422": {
            "$ref": "#/components/responses/UnprocessableEntity"
          },
          "429": {
            "$ref": "#/components/responses/RateLimitExceeded"
          }
        }
      },
      "delete": {
        "summary": "Delete a digital product",
        "description": "Permanently delete a digital product and its asset links. This does not\ndelete the underlying assets themselves — only the association between\nthe assets and the Shopify product.\n",
        "tags": [
          "Digital Products"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/AuthToken"
          },
          {
            "in": "path",
            "name": "id",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "The digital product ID"
          }
        ],
        "responses": {
          "204": {
            "description": "Digital product deleted"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimitExceeded"
          }
        }
      }
    },
    "/digital_products/{id}/notify_buyers": {
      "post": {
        "summary": "Notify all buyers of a digital product",
        "description": "Send the delivery / notification email to every buyer of a digital\nproduct, instead of calling `POST /orders/{id}/notify` once per order.\nEmails are sent in the background; the response returns the number of\norders queued without waiting for delivery. Discarded orders are skipped.\n",
        "tags": [
          "Digital Products"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/AuthToken"
          },
          {
            "in": "path",
            "name": "id",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "The digital product ID"
          }
        ],
        "responses": {
          "202": {
            "description": "Notifications queued",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "digital_product_id": {
                      "type": "string",
                      "format": "uuid"
                    },
                    "notified_count": {
                      "type": "integer",
                      "description": "Number of orders queued for notification"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "422": {
            "$ref": "#/components/responses/UnprocessableEntity"
          },
          "429": {
            "$ref": "#/components/responses/RateLimitExceeded"
          }
        }
      }
    }
  },
  "components": {
    "securitySchemes": {
      "ApiKeyAuth": {
        "type": "apiKey",
        "in": "header",
        "name": "X-Auth-Token",
        "description": "Your store's API key. Find it in Filemonk under Settings > Other > API.\n"
      }
    },
    "parameters": {
      "AuthToken": {
        "in": "header",
        "name": "X-Auth-Token",
        "required": true,
        "schema": {
          "type": "string"
        },
        "description": "API key for authentication"
      }
    },
    "headers": {
      "X-RateLimit-Limit": {
        "schema": {
          "type": "integer"
        },
        "description": "Maximum number of points allowed per window"
      },
      "X-RateLimit-Remaining": {
        "schema": {
          "type": "integer"
        },
        "description": "Number of points remaining in the current window"
      },
      "X-RateLimit-Reset": {
        "schema": {
          "type": "integer"
        },
        "description": "Unix timestamp when the current window resets"
      }
    },
    "schemas": {
      "Order": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid",
            "description": "Unique order identifier"
          },
          "name": {
            "type": "string",
            "description": "Order name/number (e.g. \"#1001\")",
            "example": "#1001"
          },
          "email": {
            "type": "string",
            "format": "email",
            "description": "Customer email address",
            "example": "customer@example.com"
          },
          "phone": {
            "type": "string",
            "nullable": true,
            "description": "Customer phone number",
            "example": "+1234567890"
          },
          "financial_status": {
            "type": "string",
            "nullable": true,
            "description": "Financial status of the order",
            "enum": [
              "paid",
              "pending",
              "partially_paid",
              "partially_refunded",
              "refunded",
              "voided",
              "authorized"
            ],
            "example": "paid"
          },
          "fulfillment_status": {
            "type": "string",
            "nullable": true,
            "description": "Fulfillment status of the order",
            "example": "fulfilled"
          },
          "total_price": {
            "type": "string",
            "nullable": true,
            "description": "Total price of the order",
            "example": "29.99"
          },
          "currency": {
            "type": "string",
            "nullable": true,
            "description": "Currency code (ISO 4217)",
            "example": "USD"
          },
          "created_at": {
            "type": "string",
            "format": "date-time",
            "description": "When the order was created in Shopify"
          },
          "download_count": {
            "type": "integer",
            "description": "Total number of downloads for this order",
            "example": 5
          }
        }
      },
      "Download": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid",
            "description": "Unique identifier for this download entry"
          },
          "product_name": {
            "type": "string",
            "description": "Name of the product this file belongs to",
            "example": "Digital Art Pack"
          },
          "file_name": {
            "type": "string",
            "description": "Name of the file",
            "example": "artwork-collection.zip"
          },
          "file_type": {
            "type": "string",
            "description": "MIME type of the file",
            "example": "application/zip"
          },
          "file_size_bytes": {
            "type": "integer",
            "description": "File size in bytes (0 for external links and licence keys)",
            "example": 52428800
          },
          "download_url": {
            "type": "string",
            "format": "uri",
            "nullable": true,
            "description": "Signed download URL (for internal files) or external URL. When the\nfile is watermarked and `watermark_status` is `ready`, this points at\nthe watermarked copy. `null` when `downloadable` is `false` or while\n`watermark_status` is `processing`.\n"
          },
          "is_watermarked": {
            "type": "boolean",
            "description": "Whether this file is personalised with the buyer's details by the shop's PDF watermarking settings"
          },
          "watermark_status": {
            "type": "string",
            "enum": [
              "not_applicable",
              "processing",
              "ready"
            ],
            "description": "`not_applicable` when the file is not watermarked, `processing` while\nthe watermarked copy is being generated (`download_url` is `null`),\nand `ready` once `download_url` serves the watermarked copy.\n"
          },
          "download_count": {
            "type": "integer",
            "description": "Number of times this file has been downloaded for this order",
            "example": 2
          },
          "download_limit": {
            "type": "integer",
            "nullable": true,
            "description": "Maximum number of downloads allowed per file. `null` means unlimited.",
            "example": 5
          },
          "download_limit_reached": {
            "type": "boolean",
            "description": "Whether the download limit has been reached"
          },
          "expires_at": {
            "type": "string",
            "format": "date-time",
            "nullable": true,
            "description": "When the download expires. `null` means no expiration date is set.\n"
          },
          "download_expired": {
            "type": "boolean",
            "description": "Whether the download has expired based on the store's download limit days setting"
          },
          "access_revoked": {
            "type": "boolean",
            "description": "Whether access to this file has been revoked (e.g. due to refund or cancellation)"
          },
          "is_preorder": {
            "type": "boolean",
            "description": "Whether this file belongs to a preorder product"
          },
          "preorder_available_at": {
            "type": "string",
            "format": "date-time",
            "nullable": true,
            "description": "When the preorder content becomes available. `null` if not a preorder."
          },
          "preorder_released": {
            "type": "boolean",
            "description": "Whether the preorder has been released (always `true` for non-preorder items)"
          },
          "is_licence_key": {
            "type": "boolean",
            "description": "Whether this download is a licence key rather than a file"
          },
          "licence_key": {
            "type": "string",
            "nullable": true,
            "description": "The licence key value if this is a licence key type and one has been assigned. `null` otherwise."
          },
          "downloadable": {
            "type": "boolean",
            "description": "Master flag indicating whether the file can be downloaded right now.\n`true` only when: not expired, download limit not reached, access not\nrevoked, and preorder released (if applicable).\n"
          }
        }
      },
      "Asset": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid",
            "description": "Unique asset identifier",
            "example": "a1b2c3d4-e5f6-7890-abcd-ef1234567890"
          },
          "type": {
            "type": "string",
            "enum": [
              "internal",
              "external",
              "licence"
            ],
            "description": "The asset type. `internal` for uploaded files, `external` for URL-based assets, `licence` for licence key assets.",
            "example": "external"
          },
          "name": {
            "type": "string",
            "nullable": true,
            "description": "Display name of the asset.",
            "example": "Photoshop Brushes Pack"
          },
          "filename": {
            "type": "string",
            "nullable": true,
            "description": "Filename of the asset (derived from the file or URL).",
            "example": "brushes-pack-v2.zip"
          },
          "external_url": {
            "type": "string",
            "format": "uri",
            "nullable": true,
            "description": "The external URL for `external` type assets. `null` for other types.",
            "example": "https://cdn.example.com/files/brushes-pack-v2.zip"
          },
          "content_type": {
            "type": "string",
            "nullable": true,
            "description": "MIME type of the asset.",
            "example": "application/zip"
          },
          "byte_size": {
            "type": "integer",
            "description": "File size in bytes. `0` for external and licence assets.",
            "example": 0
          },
          "watermark_asset": {
            "type": "boolean",
            "description": "Whether watermarking is enabled for this asset (PDFs only).",
            "example": false
          },
          "autogenerate_licence_key": {
            "type": "boolean",
            "description": "Whether a licence key is auto-generated when this asset is purchased.",
            "example": false
          },
          "created_at": {
            "type": "string",
            "format": "date-time",
            "description": "When the asset was created."
          },
          "updated_at": {
            "type": "string",
            "format": "date-time",
            "description": "When the asset was last updated."
          }
        }
      },
      "DigitalProduct": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid",
            "description": "Unique digital product identifier",
            "example": "b2c3d4e5-f6a7-8901-bcde-f12345678901"
          },
          "shopify_product_id": {
            "type": "integer",
            "nullable": true,
            "description": "The numeric Shopify product ID this digital product is linked to.",
            "example": 8847261098265
          },
          "shopify_variant_id": {
            "type": "integer",
            "nullable": true,
            "description": "The numeric Shopify variant ID, if linked to a specific variant. `null` if linked at the product level.",
            "example": 46012345678921
          },
          "assets": {
            "type": "array",
            "description": "The assets attached to this digital product.",
            "items": {
              "$ref": "#/components/schemas/AssetSummary"
            }
          },
          "upsert_always": {
            "type": "boolean",
            "description": "Whether a new copy of assets is created for each order.",
            "example": false
          },
          "custom_message": {
            "type": "string",
            "nullable": true,
            "description": "Custom message shown on the download page.",
            "example": "Thank you for your purchase!"
          },
          "preorder_enabled": {
            "type": "boolean",
            "description": "Whether this is a preorder product.",
            "example": false
          },
          "preorder_launch_date": {
            "type": "string",
            "format": "date-time",
            "nullable": true,
            "description": "When the preorder content becomes available. `null` if not a preorder."
          },
          "preorder_subject": {
            "type": "string",
            "nullable": true,
            "description": "Email subject for the preorder notification."
          },
          "preorder_heading": {
            "type": "string",
            "nullable": true,
            "description": "Heading shown on the download page while in preorder."
          },
          "preorder_description": {
            "type": "string",
            "nullable": true,
            "description": "Description shown on the download page while in preorder."
          },
          "created_at": {
            "type": "string",
            "format": "date-time",
            "description": "When the digital product was created."
          },
          "updated_at": {
            "type": "string",
            "format": "date-time",
            "description": "When the digital product was last updated."
          }
        }
      },
      "AssetSummary": {
        "type": "object",
        "description": "A compact representation of an asset as returned inside a digital product.",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid",
            "description": "Asset identifier"
          },
          "type": {
            "type": "string",
            "enum": [
              "internal",
              "external",
              "licence"
            ],
            "description": "The asset type."
          },
          "name": {
            "type": "string",
            "nullable": true,
            "description": "Display name of the asset."
          },
          "filename": {
            "type": "string",
            "nullable": true,
            "description": "Filename of the asset."
          },
          "external_url": {
            "type": "string",
            "format": "uri",
            "nullable": true,
            "description": "External URL for `external` type assets."
          },
          "content_type": {
            "type": "string",
            "nullable": true,
            "description": "MIME type of the asset."
          },
          "byte_size": {
            "type": "integer",
            "description": "File size in bytes."
          },
          "created_at": {
            "type": "string",
            "format": "date-time",
            "description": "When the asset was created."
          }
        }
      },
      "Pagination": {
        "type": "object",
        "properties": {
          "current_page": {
            "type": "integer",
            "description": "Current page number",
            "example": 1
          },
          "per_page": {
            "type": "integer",
            "description": "Number of items per page",
            "example": 25
          },
          "total_count": {
            "type": "integer",
            "description": "Total number of items across all pages",
            "example": 142
          },
          "total_pages": {
            "type": "integer",
            "description": "Total number of pages",
            "example": 6
          }
        }
      },
      "Error": {
        "type": "object",
        "properties": {
          "error": {
            "type": "string",
            "description": "Error message"
          }
        }
      },
      "ValidationError": {
        "type": "object",
        "properties": {
          "errors": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "List of validation error messages",
            "example": [
              "External url can't be blank"
            ]
          }
        }
      },
      "RateLimitError": {
        "type": "object",
        "properties": {
          "error": {
            "type": "string",
            "example": "Rate limit exceeded"
          },
          "retry_after": {
            "type": "integer",
            "description": "Seconds to wait before retrying"
          },
          "message": {
            "type": "string",
            "description": "Human-readable rate limit explanation"
          }
        }
      }
    },
    "responses": {
      "Unauthorized": {
        "description": "Authentication failed",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            },
            "examples": {
              "missing_key": {
                "summary": "Missing API key",
                "value": {
                  "error": "API key is required"
                }
              },
              "invalid_key": {
                "summary": "Invalid API key",
                "value": {
                  "error": "Invalid API key"
                }
              }
            }
          }
        }
      },
      "NotFound": {
        "description": "Resource not found",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            },
            "example": {
              "error": "Resource not found"
            }
          }
        }
      },
      "UnprocessableEntity": {
        "description": "Validation failed",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/ValidationError"
            }
          }
        }
      },
      "RateLimitExceeded": {
        "description": "Rate limit exceeded",
        "headers": {
          "X-RateLimit-Limit": {
            "$ref": "#/components/headers/X-RateLimit-Limit"
          },
          "X-RateLimit-Remaining": {
            "$ref": "#/components/headers/X-RateLimit-Remaining"
          },
          "X-RateLimit-Reset": {
            "$ref": "#/components/headers/X-RateLimit-Reset"
          },
          "Retry-After": {
            "schema": {
              "type": "integer"
            },
            "description": "Seconds to wait before retrying"
          }
        },
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/RateLimitError"
            }
          }
        }
      }
    }
  }
}