API & Webhooks

Retrieve your transfers and statistics programmatically, and get a notification the moment someone downloads your files. Read-only REST + HMAC-signed webhooks.

1. Getting started

Create an API key on your account page (part of every paid plan). The key is shown only once — keep it safe. Send it with every request:

Authorization: Bearer dlk_live_xxxxxxxxxxxxxxxxxxxx

2. Endpoints

Base URL: https://downloadlink.nl/api/v1. All responses are JSON and limited to your own account.

GET /api/v1/me

Account + storage usage.

curl -H "Authorization: Bearer dlk_live_xxx" \
     https://downloadlink.nl/api/v1/me

{
  "email": "jij@bedrijf.nl",
  "is_trial": false,
  "plan": "100 GB",
  "storage": { "used_bytes": 5242880, "quota_bytes": 107374182400, "percent": 0 }
}

GET /api/v1/transfers

Your transfers (newest first). Parameter ?limit= (1–200, default 50).

{
  "count": 1,
  "transfers": [
    {
      "token": "a1b2c3d4e5f6a7b8",
      "title": "Quote Q3",
      "created_at": "2026-06-17T09:12:00+00:00",
      "expires_at": "2026-06-24T09:12:00+00:00",
      "unlimited": false,
      "revoked": false,
      "files": 3,
      "size_bytes": 8412300,
      "downloads": 2,
      "recipients": 1,
      "url": "https://downloadlink.nl/p/a1b2c3d4e5f6a7b8"
    }
  ]
}

GET /api/v1/transfers/<token>

A single transfer: metadata, the files, and recent download activity (who/when/which file).

{
  "token": "a1b2c3d4e5f6a7b8",
  "title": "Quote Q3",
  "created_at": "2026-06-17T09:12:00+00:00",
  "expires_at": "2026-06-24T09:12:00+00:00",
  "unlimited": false,
  "revoked": false,
  "max_downloads": null,
  "downloads_used": 0,
  "size_bytes": 8412300,
  "files": [
    { "name": "quote.pdf", "size_bytes": 412300 }
  ],
  "downloads": 2,
  "url": "https://downloadlink.nl/p/a1b2c3d4e5f6a7b8",
  "activity": [
    {
      "downloaded_at": "2026-06-17T10:30:00+00:00",
      "type": "zip",
      "file": "(whole package / zip)",
      "ip": "203.0.113.7"
    },
    {
      "downloaded_at": "2026-06-17T09:58:00+00:00",
      "type": "file",
      "file": "quote.pdf",
      "ip": "198.51.100.24"
    }
  ]
}

Note: ip is the full IP address of the person who downloaded — personal data of your recipient, not an anonymised figure. You receive it in full, just as in the activity overview, the downloads CSV and the proof of delivery (PDF); it is there so you can demonstrate that a package has been retrieved. If you process or store it in your own system, include it in your own privacy statement. We return the 100 most recent records; downloads counts those records. We keep them for a maximum of 12 months — after that, they can no longer be retrieved, not even via the API. downloads_used is the counter for the download limit and remains 0 as long as max_downloads is empty.

Rate limit: 120 requests per minute per key (HTTP 429 when exceeded).

2b. Creating a transfer (write)

Deliver a file via your own branded download page, straight from your system — a generated report or invoice, for example:

curl -X POST https://downloadlink.nl/api/v1/transfers \
  -H "Authorization: Bearer <sleutel>" \
  -F "file=@rapport.pdf" \
  -F "title=Rapport week 30" \
  -F "expiry_days=30"        # of 'never' voor onbeperkt geldig

# optioneel: -F "password=geheim" voor een wachtwoord op de downloadpagina
HTTP 201
{
  "ok": true,
  "token": "a1b2c3d4e5f6a7b8",
  "url": "https://downloadlink.nl/p/a1b2c3d4e5f6a7b8",
  "expires_at": "2026-08-22T10:30:00+00:00",
  "unlimited": false
}

Error paths: 402 payment_required (the write API is part of the paid plans), 402 quota_exceeded (storage full), 413 (file larger than the API limit — use the upload page for that). The download page branding follows the account the key belongs to.

3. Webhooks

Register an https-URL on your account page (the "Test" button immediately sends a ping-event so you can check your integration). As soon as someone downloads your transfer, we send a POST with this event:

POST https://jouw-server.nl/webhooks/downloadlink
X-Downloadlink-Event: transfer.downloaded
X-Downloadlink-Signature: sha256=<hmac>
Content-Type: application/json

{
  "event": "transfer.downloaded",
  "delivered_at": "2026-06-17T10:30:00+00:00",
  "data": {
    "token": "a1b2c3d4e5f6a7b8",
    "title": "Quote Q3",
    "url": "https://downloadlink.nl/p/a1b2c3d4e5f6a7b8",
    "download_type": "zip",
    "downloaded_at": "2026-06-17T10:30:00+00:00"
  }
}

Verifying the signature

Compute HMAC-SHA256 over the raw request body with your webhook secret and compare it constant-time against the header. Ignore messages that do not match.

Python

import hmac, hashlib

def is_valid(secret: str, raw_body: bytes, header: str) -> bool:
    expected = "sha256=" + hmac.new(secret.encode(), raw_body, hashlib.sha256).hexdigest()
    return hmac.compare_digest(expected, header or "")

Node.js

const crypto = require('crypto');

function isValid(secret, rawBody, header) {
  const expected = 'sha256=' + crypto.createHmac('sha256', secret).update(rawBody).digest('hex');
  const a = Buffer.from(expected), b = Buffer.from(header || '');
  return a.length === b.length && crypto.timingSafeEqual(a, b);
}

Reply with HTTP 2xx to confirm delivery. After 15 failed attempts we switch the webhook off; a successful test (button on your account page) turns it back on. Only public https URLs are allowed.