{
 "openapi": "3.1.0",
 "info": {
  "title": "Downloadlink.nl API",
  "version": "1",
  "description": "Share files with a download link. Every response is limited to the account that owns the key. Keys are created on the account page (https://downloadlink.nl/account#api) and are part of every paid plan (prices: https://downloadlink.nl/prijzen). A key has scopes (lezen, maken, mailen) and expires after at most 365 days. Rate limit: 120 requests per 60 seconds per key (429 with Retry-After). The MCP server at https://downloadlink.nl/mcp uses the same keys; its description is at https://downloadlink.nl/developers/mcp.json. Documentation: https://downloadlink.nl/developers."
 },
 "servers": [
  {
   "url": "https://downloadlink.nl/api/v1"
  }
 ],
 "components": {
  "securitySchemes": {
   "apiKey": {
    "type": "http",
    "scheme": "bearer",
    "description": "Authorization: Bearer dlk_live_..."
   }
  },
  "schemas": {
   "Error": {
    "type": "object",
    "required": [
     "error"
    ],
    "properties": {
     "error": {
      "type": "string"
     },
     "message": {
      "type": "string"
     },
     "required_scope": {
      "type": "string",
      "enum": [
       "lezen",
       "maken",
       "mailen"
      ]
     },
     "needed": {
      "type": "string",
      "description": "Present when a plan is the reason: what is needed."
     },
     "pricing_url": {
      "type": "string",
      "format": "uri"
     }
    }
   },
   "Me": {
    "type": "object",
    "properties": {
     "email": {
      "type": "string"
     },
     "tenant": {
      "type": "string"
     },
     "is_trial": {
      "type": "boolean"
     },
     "member_since": {
      "type": [
       "string",
       "null"
      ]
     },
     "plan": {
      "type": [
       "string",
       "null"
      ]
     },
     "storage": {
      "type": "object",
      "properties": {
       "used_bytes": {
        "type": [
         "integer",
         "null"
        ]
       },
       "quota_bytes": {
        "type": [
         "integer",
         "null"
        ]
       },
       "percent": {
        "type": [
         "number",
         "null"
        ]
       }
      }
     },
     "key": {
      "type": "object",
      "properties": {
       "scopes": {
        "type": "array",
        "items": {
         "type": "string",
         "enum": [
          "lezen",
          "maken",
          "mailen"
         ]
        }
       },
       "expires_at": {
        "type": [
         "string",
         "null"
        ],
        "format": "date-time"
       },
       "type": {
        "type": "string",
        "enum": [
         "api",
         "assistent"
        ]
       }
      }
     },
     "plan_info": {
      "type": "object",
      "description": "What the account's plan includes, from the same checks as the website. A value of null means it could not be determined.",
      "properties": {
       "paid_plan": {
        "type": [
         "boolean",
         "null"
        ]
       },
       "features": {
        "type": "object",
        "properties": {
         "api_and_mcp": {
          "type": [
           "boolean",
           "null"
          ]
         },
         "transfer_password_and_long_validity": {
          "type": [
           "boolean",
           "null"
          ]
         },
         "verified_recipients": {
          "type": [
           "boolean",
           "null"
          ]
         },
         "browser_tools_unlimited": {
          "type": [
           "boolean",
           "null"
          ]
         }
        }
       },
       "free_account": {
        "type": "object",
        "properties": {
         "storage_bytes": {
          "type": "integer",
          "example": 53687091200
         },
         "max_bytes_per_transfer": {
          "type": "integer",
          "example": 53687091200
         },
         "max_transfer_validity_days": {
          "type": "integer",
          "example": 30
         },
         "browser_tool_uses_per_category": {
          "type": "integer",
          "example": 10
         }
        }
       },
       "pricing_url": {
        "type": "string",
        "format": "uri",
        "example": "https://downloadlink.nl/prijzen"
       },
       "note": {
        "type": "string"
       }
      }
     }
    }
   },
   "TransferSummary": {
    "type": "object",
    "properties": {
     "token": {
      "type": "string"
     },
     "title": {
      "type": [
       "string",
       "null"
      ]
     },
     "title_derived": {
      "type": "boolean",
      "description": "True when the transfer has no title and 'title' is derived from its files for display ('first.pdf + 28 andere'); the stored title stays empty."
     },
     "created_at": {
      "type": "string"
     },
     "expires_at": {
      "type": [
       "string",
       "null"
      ]
     },
     "unlimited": {
      "type": "boolean"
     },
     "revoked": {
      "type": "boolean"
     },
     "files": {
      "type": "integer"
     },
     "size_bytes": {
      "type": "integer"
     },
     "downloads": {
      "type": "integer"
     },
     "recipients": {
      "type": "integer"
     },
     "url": {
      "type": "string",
      "format": "uri"
     }
    }
   },
   "TransferDetail": {
    "type": "object",
    "properties": {
     "token": {
      "type": "string"
     },
     "title": {
      "type": [
       "string",
       "null"
      ]
     },
     "created_at": {
      "type": "string"
     },
     "expires_at": {
      "type": [
       "string",
       "null"
      ]
     },
     "unlimited": {
      "type": "boolean"
     },
     "revoked": {
      "type": "boolean"
     },
     "max_downloads": {
      "type": [
       "integer",
       "null"
      ]
     },
     "downloads_used": {
      "type": "integer"
     },
     "size_bytes": {
      "type": "integer"
     },
     "files": {
      "type": "array",
      "items": {
       "type": "object",
       "properties": {
        "name": {
         "type": "string"
        },
        "size_bytes": {
         "type": "integer"
        }
       }
      }
     },
     "downloads": {
      "type": "integer"
     },
     "url": {
      "type": "string",
      "format": "uri"
     },
     "activity": {
      "type": "array",
      "description": "The 100 most recent downloads. ip is the full IP address of the recipient (personal data).",
      "items": {
       "type": "object",
       "properties": {
        "downloaded_at": {
         "type": "string"
        },
        "type": {
         "type": "string",
         "enum": [
          "file",
          "zip"
         ]
        },
        "file": {
         "type": "string"
        },
        "ip": {
         "type": "string"
        }
       }
      }
     }
    }
   },
   "TransferCreated": {
    "type": "object",
    "properties": {
     "ok": {
      "type": "boolean"
     },
     "token": {
      "type": "string"
     },
     "url": {
      "type": "string",
      "format": "uri"
     },
     "expires_at": {
      "type": [
       "string",
       "null"
      ]
     },
     "unlimited": {
      "type": "boolean"
     }
    }
   }
  }
 },
 "x-scopes": {
  "lezen": "Read: the account (/api/v1/me) and its own transfers.",
  "maken": "Create: new transfers (and, through the MCP server, file request links, extending and revoking).",
  "mailen": "Send email on the account's behalf (the MCP tools send_transfer, send_folder_link, remind_recipients and request_signature). Only for an API key and only with two-step verification on; never through OAuth."
 },
 "paths": {
  "/me": {
   "get": {
    "summary": "The account, its plan and storage, and what this key may do.",
    "security": [
     {
      "apiKey": []
     }
    ],
    "x-required-scope": "lezen",
    "responses": {
     "200": {
      "description": "The account.",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Me"
        }
       }
      }
     },
     "401": {
      "description": "No, an unknown, revoked or expired key (error unauthorized or key_expired).",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Error"
        }
       }
      }
     },
     "402": {
      "description": "The account has no active paid plan (error payment_required; fields needed and pricing_url).",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Error"
        }
       }
      }
     },
     "403": {
      "description": "The key lacks the scope of this endpoint (error insufficient_scope, field required_scope).",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Error"
        }
       }
      }
     },
     "429": {
      "description": "Too many requests (error rate_limited); wait the seconds in Retry-After.",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Error"
        }
       }
      }
     }
    }
   }
  },
  "/transfers": {
   "get": {
    "summary": "The account's own transfers, newest first.",
    "security": [
     {
      "apiKey": []
     }
    ],
    "x-required-scope": "lezen",
    "responses": {
     "200": {
      "description": "A list.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "properties": {
          "count": {
           "type": "integer"
          },
          "transfers": {
           "type": "array",
           "items": {
            "$ref": "#/components/schemas/TransferSummary"
           }
          }
         }
        }
       }
      }
     },
     "401": {
      "description": "No, an unknown, revoked or expired key (error unauthorized or key_expired).",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Error"
        }
       }
      }
     },
     "402": {
      "description": "The account has no active paid plan (error payment_required; fields needed and pricing_url).",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Error"
        }
       }
      }
     },
     "403": {
      "description": "The key lacks the scope of this endpoint (error insufficient_scope, field required_scope).",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Error"
        }
       }
      }
     },
     "429": {
      "description": "Too many requests (error rate_limited); wait the seconds in Retry-After.",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Error"
        }
       }
      }
     }
    },
    "parameters": [
     {
      "name": "limit",
      "in": "query",
      "schema": {
       "type": "integer",
       "minimum": 1,
       "maximum": 200,
       "default": 50
      }
     }
    ]
   },
   "post": {
    "summary": "Create a transfer with one file and get its download link.",
    "security": [
     {
      "apiKey": []
     }
    ],
    "x-required-scope": "maken",
    "responses": {
     "201": {
      "description": "Created.",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/TransferCreated"
        }
       }
      }
     },
     "400": {
      "description": "A field is not valid (for example password_too_short or expiry_not_allowed).",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Error"
        }
       }
      }
     },
     "413": {
      "description": "The request is larger than 64 MB; use the upload page for larger files."
     },
     "401": {
      "description": "No, an unknown, revoked or expired key (error unauthorized or key_expired).",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Error"
        }
       }
      }
     },
     "402": {
      "description": "The account has no active paid plan (error payment_required; fields needed and pricing_url).",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Error"
        }
       }
      }
     },
     "403": {
      "description": "The key lacks the scope of this endpoint (error insufficient_scope, field required_scope).",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Error"
        }
       }
      }
     },
     "429": {
      "description": "Too many requests (error rate_limited); wait the seconds in Retry-After.",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Error"
        }
       }
      }
     }
    },
    "requestBody": {
     "required": true,
     "content": {
      "multipart/form-data": {
       "schema": {
        "type": "object",
        "required": [
         "file"
        ],
        "properties": {
         "file": {
          "type": "string",
          "format": "binary"
         },
         "title": {
          "type": "string",
          "maxLength": 120
         },
         "filename": {
          "type": "string",
          "maxLength": 200
         },
         "expiry_days": {
          "type": "string",
          "description": "A whole number of days, or 'never'. A key of type 'assistent' gets 30 days without this field, at most 90, and 'never' is refused (expiry_not_allowed)."
         },
         "password": {
          "type": "string",
          "minLength": 6,
          "maxLength": 200
         }
        }
       }
      }
     }
    },
    "description": "Counts in the transfer limit of the website: at most 50 new transfers per 3600 seconds, per key and per account together."
   }
  },
  "/transfers/{token}": {
   "get": {
    "summary": "One of the account's transfers: its files and recent downloads.",
    "security": [
     {
      "apiKey": []
     }
    ],
    "x-required-scope": "lezen",
    "responses": {
     "200": {
      "description": "The transfer.",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/TransferDetail"
        }
       }
      }
     },
     "404": {
      "description": "Not found, or not a transfer of this account.",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Error"
        }
       }
      }
     },
     "401": {
      "description": "No, an unknown, revoked or expired key (error unauthorized or key_expired).",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Error"
        }
       }
      }
     },
     "402": {
      "description": "The account has no active paid plan (error payment_required; fields needed and pricing_url).",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Error"
        }
       }
      }
     },
     "403": {
      "description": "The key lacks the scope of this endpoint (error insufficient_scope, field required_scope).",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Error"
        }
       }
      }
     },
     "429": {
      "description": "Too many requests (error rate_limited); wait the seconds in Retry-After.",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Error"
        }
       }
      }
     }
    },
    "parameters": [
     {
      "name": "token",
      "in": "path",
      "required": true,
      "schema": {
       "type": "string",
       "pattern": "^[a-f0-9]{10}$|^[a-f0-9]{16}$"
      }
     }
    ]
   }
  },
  "/feedback": {
   "post": {
    "summary": "Report to downloadlink.nl that something is missing, unclear or wrong, or an idea.",
    "security": [
     {
      "apiKey": []
     }
    ],
    "x-required-scope": "lezen",
    "responses": {
     "201": {
      "description": "Passed on (at least one point stored). No reply is sent to the user.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "properties": {
          "ok": {
           "type": "boolean"
          },
          "status": {
           "type": "string",
           "enum": [
            "passed_on",
            "duplicate"
           ]
          },
          "message": {
           "type": "string"
          },
          "passed_on": {
           "type": "integer"
          },
          "duplicates": {
           "type": "integer"
          },
          "not_stored_rate_limited": {
           "type": "integer"
          },
          "retry_after": {
           "type": "integer"
          },
          "results": {
           "type": "array",
           "items": {
            "type": "object",
            "properties": {
             "index": {
              "type": "integer"
             },
             "status": {
              "type": "string",
              "enum": [
               "passed_on",
               "duplicate",
               "rate_limited"
              ]
             }
            }
           }
          }
         }
        }
       }
      }
     },
     "200": {
      "description": "Nothing new stored: every point was already reported in the last 7 days (status duplicate).",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "properties": {
          "ok": {
           "type": "boolean"
          },
          "status": {
           "type": "string",
           "enum": [
            "passed_on",
            "duplicate"
           ]
          },
          "message": {
           "type": "string"
          },
          "passed_on": {
           "type": "integer"
          },
          "duplicates": {
           "type": "integer"
          },
          "not_stored_rate_limited": {
           "type": "integer"
          },
          "retry_after": {
           "type": "integer"
          },
          "results": {
           "type": "array",
           "items": {
            "type": "object",
            "properties": {
             "index": {
              "type": "integer"
             },
             "status": {
              "type": "string",
              "enum": [
               "passed_on",
               "duplicate",
               "rate_limited"
              ]
             }
            }
           }
          }
         }
        }
       }
      }
     },
     "400": {
      "description": "A field is not valid (error invalid_arguments).",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Error"
        }
       }
      }
     },
     "401": {
      "description": "No, an unknown, revoked or expired key (error unauthorized or key_expired).",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Error"
        }
       }
      }
     },
     "402": {
      "description": "The account has no active paid plan (error payment_required; fields needed and pricing_url).",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Error"
        }
       }
      }
     },
     "403": {
      "description": "The key lacks the scope of this endpoint (error insufficient_scope, field required_scope).",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Error"
        }
       }
      }
     },
     "429": {
      "description": "Too many requests (error rate_limited); wait the seconds in Retry-After.",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Error"
        }
       }
      }
     }
    },
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {
         "soort": {
          "type": "string",
          "enum": [
           "ontbreekt",
           "onduidelijk",
           "fout",
           "idee"
          ],
          "description": "ontbreekt = missing, onduidelijk = unclear, fout = wrong, idee = idea."
         },
         "wat": {
          "type": "string",
          "minLength": 1,
          "maxLength": 500,
          "description": "Short description of what is missing or could be better."
         },
         "taak": {
          "type": "string",
          "minLength": 1,
          "maxLength": 300,
          "description": "What the user was trying to do."
         },
         "tool": {
          "type": "string",
          "maxLength": 80,
          "description": "Optional: the tool or tool page it is about, e.g. 'pdf/organiseren' or 'extend_transfer'."
         },
         "punten": {
          "type": "array",
          "minItems": 1,
          "maxItems": 10,
          "items": {
           "type": "object",
           "properties": {
            "soort": {
             "type": "string",
             "enum": [
              "ontbreekt",
              "onduidelijk",
              "fout",
              "idee"
             ]
            },
            "wat": {
             "type": "string",
             "minLength": 1,
             "maxLength": 500
            },
            "taak": {
             "type": "string",
             "minLength": 1,
             "maxLength": 300
            },
            "tool": {
             "type": "string",
             "maxLength": 80
            }
           },
           "required": [
            "soort",
            "wat",
            "taak"
           ],
           "additionalProperties": false
          },
          "description": "Several points in one call (instead of soort, wat, taak and tool); each point is stored as its own suggestion."
         }
        },
        "additionalProperties": false,
        "anyOf": [
         {
          "required": [
           "soort",
           "wat",
           "taak"
          ]
         },
         {
          "required": [
           "punten"
          ]
         }
        ]
       }
      }
     }
    },
    "description": "Without personal data, file contents or names of customers. One point (soort, wat, taak, tool) or punten: a list of at most 10. At most 40 per account and 40 per key per day (429). A repeat of the same text for the same tool within 7 days is not stored again. Kept 365 days, without an IP address."
   }
  }
 },
 "x-local-helper": {
  "available": true,
  "version": "1.8.10",
  "about": "Running a tool (merging PDFs, compressing images or videos, converting files) needs the local helper on the user's computer (Claude Desktop, Claude Code, Cursor or VS Code, with Chrome or Edge). The files stay on that computer. On claude.ai on the web, a phone or ChatGPT on the web a tool cannot run there; give the user the link to the tool page.",
  "info_url": "https://downloadlink.nl/koppelen/helper",
  "requires": "Node.js 18 or newer, and Google Chrome or Microsoft Edge on the same computer.",
  "install": {
   "claude_code": "claude mcp add downloadlink --scope user \"--\" npx -y https://downloadlink.nl/helper/downloadlink-helper-1.8.10.tgz",
   "vscode": "vscode:mcp/install?%7B%22name%22%3A%22downloadlink%22%2C%22type%22%3A%22stdio%22%2C%22command%22%3A%22npx%22%2C%22args%22%3A%5B%22-y%22%2C%22https%3A%2F%2Fdownloadlink.nl%2Fhelper%2Fdownloadlink-helper-1.8.10.tgz%22%5D%7D",
   "cursor": "cursor://anysphere.cursor-deeplink/mcp/install?name=downloadlink&config=eyJjb21tYW5kIjoibnB4IiwiYXJncyI6WyIteSIsImh0dHBzOi8vZG93bmxvYWRsaW5rLm5sL2hlbHBlci9kb3dubG9hZGxpbmstaGVscGVyLTEuOC4xMC50Z3oiXX0%3D",
   "mcp_json": {
    "mcpServers": {
     "downloadlink": {
      "command": "npx",
      "args": [
       "-y",
       "https://downloadlink.nl/helper/downloadlink-helper-1.8.10.tgz"
      ]
     }
    }
   },
   "claude_desktop_mcpb": "https://downloadlink.nl/helper/downloadlink-helper-1.8.10.mcpb"
  },
  "package": {
   "url": "https://downloadlink.nl/helper/downloadlink-helper-1.8.10.tgz",
   "sha256": "47f7efb0b3c51dc36f01d01628af1d08e756b0dafaa8b033896221039f8f005b",
   "size_bytes": 133371
  }
 }
}