API & Webhooks

Haal je transfers en statistieken programmatisch op, maak transfers aan vanuit je eigen systeem, en ontvang een melding zodra iemand je bestanden downloadt. REST-API met rechten per sleutel + HMAC-gesigneerde webhooks.

1. Aan de slag

Maak een API-sleutel aan op je accountpagina (onderdeel van elk betaald plan). De sleutel wordt één keer getoond — bewaar 'm veilig. Stuur 'm mee in elke aanvraag:

Authorization: Bearer dlk_live_xxxxxxxxxxxxxxxxxxxx

Rechten en verloopdatum

Bij het maken kies je wat de sleutel mag. Geef een sleutel niet meer rechten dan je koppeling nodig heeft.

  • Lezen (lezen) — GET /api/v1/me, GET /api/v1/transfers, GET /api/v1/transfers/<token>.
  • Maken (maken) — POST /api/v1/transfers.
  • Mailen (mailen) — mail namens jou versturen met send_transfer, send_folder_link, remind_recipients en request_signature van de MCP-server (zie 2c); kan alleen gekozen worden met tweestapsverificatie aan en werkt alleen zolang die aanstaat. De REST-API mailt niet.

Een nieuwe sleutel verloopt: standaard na 90 dagen, hoogstens na 365. Het maken bevestig je met je wachtwoord (of een link per e-mail) en, als je tweestapsverificatie aanstaat, met de code uit je app; je krijgt daarna een mail. Sleutels die je eerder maakte hebben geen verloopdatum en houden lezen en maken; je accountpagina waarschuwt daarbij.

Wat je sleutel mag en tot wanneer, staat in het antwoord van /api/v1/me (veld key).

2. Endpoints

Basis-URL: https://downloadlink.nl/api/v1. Alle responses zijn JSON en beperkt tot je eigen account.

De volledige beschrijving staat als OpenAPI 3.1 op /api/v1/openapi.json.

GET /api/v1/me

Account + opslagverbruik.

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 },
  "key": { "scopes": ["lezen", "maken"], "expires_at": "2027-01-11T09:12:00+00:00", "type": "api" }
}

GET /api/v1/transfers

Je transfers (nieuwste eerst). Parameter ?limit= (1–200, standaard 50).

{
  "count": 1,
  "transfers": [
    {
      "token": "a1b2c3d4e5f6a7b8",
      "title": "Offerte 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>

Eén transfer: metadata, de bestanden, én recente download-activiteit (wie/wanneer/welk bestand).

{
  "token": "a1b2c3d4e5f6a7b8",
  "title": "Offerte 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": "offerte.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": "(hele pakket / zip)",
      "ip": "203.0.113.7"
    },
    {
      "downloaded_at": "2026-06-17T09:58:00+00:00",
      "type": "file",
      "file": "offerte.pdf",
      "ip": "198.51.100.24"
    }
  ]
}

Let op: ip is het volledige IP-adres van degene die downloadde — een persoonsgegeven van je ontvanger, geen geanonimiseerd cijfer. Je krijgt het onverkort terug, net als in het activiteitenoverzicht, de downloads-CSV en het leveringsbewijs (PDF); het staat er om te kunnen aantonen dat een pakket is opgehaald. Verwerk of bewaar je het in je eigen systeem, neem het dan op in je eigen privacyverklaring. We geven de 100 recentste registraties terug; downloads telt die regels. We bewaren ze maximaal 12 maanden — daarna zijn ze ook via de API niet meer op te halen. downloads_used is de teller van de downloadlimiet en blijft 0 zolang max_downloads leeg is.

Rate limit: 120 verzoeken per minuut per sleutel (HTTP 429 bij overschrijding).

Foutcodes

  • 401 unauthorized — geen, onbekende of ingetrokken sleutel.
  • 401 key_expired — de sleutel is verlopen (veld expired_at); maak een nieuwe op je accountpagina.
  • 402 payment_required — je account heeft geen actief betaald plan (meer); je sleutels blijven bewaard.
  • 403 insufficient_scope — de sleutel heeft het recht niet (veld required_scope).
  • 429 rate_limited — te veel verzoeken, of meer dan 50 nieuwe transfers per uur (per sleutel en per account samen); wacht het aantal seconden uit Retry-After.

2b. Transfer aanmaken (schrijven)

Lever een bestand af via je eigen gebrande downloadpagina, direct vanuit je systeem — bijvoorbeeld een gegenereerd rapport of factuur:

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
}

Foutpaden: 402 payment_required (schrijf-API zit in betaalde plannen), 402 quota_exceeded (opslag vol), 413 (bestand groter dan de API-limiet — gebruik daarvoor de uploadpagina). De branding van de downloadpagina volgt het account van de sleutel.

2c. MCP-server

Geen ontwikkelaar? Zo koppel je je AI-assistent in een paar klikken: AI-assistent koppelen, stap voor stap.

Voor een MCP-client (Model Context Protocol, transport streamable HTTP, zonder sessie). Gebruik dezelfde sleutel in de kop Authorization; een sleutel in de URL wordt geweigerd.

POST https://downloadlink.nl/mcp
Authorization: Bearer dlk_live_xxx
  • lezen — whoami, list_transfers, get_transfer, list_received, get_received, search_tools, find_music, find_sfx, find_templates, how_to, suggest_improvement, get_transfer_activity, get_delivery_proof, list_contacts, list_send_groups, get_branding, export_account, list_signature_requests, list_folders, get_folder, get_folder_feedback, get_changes_since, read_planning, list_rules.
  • maken — create_transfer_from_text, start_upload, complete_upload, create_request_link, extend_transfer, revoke_transfer, update_transfer, restore_transfer, delete_transfer, close_request_link, set_request_link_name, delete_received, save_contact, delete_contact, save_send_group, delete_send_group, set_branding, set_logo, set_tool_style, set_expiry_reminders, cancel_signature_request, create_folder, create_folder_dir, upload_to_folder, delete_folder_item, set_transfer_options, set_folder_options, organize_folder, save_contacts, set_defaults, save_request_template, delete_request_template, describe_tool, run_tool, edit_document, tab_job, open_file, set_rule.
  • mailen — send_transfer, send_folder_link, request_signature, remind_recipients.

Er wordt alleen gemaild met send_transfer, send_folder_link, remind_recipients en request_signature: dat vraagt een sleutel met het recht mailen, en dat recht geldt alleen zolang de tweestapsverificatie van het account aanstaat. Een gereedschap waarvoor de sleutel het recht niet heeft, staat niet in tools/list en geeft bij een aanroep de toolfout insufficient_scope.

Adres, rechten en gereedschappen als JSON: /developers/mcp.json.

whoami en /api/v1/me noemen in plan_info wat het plan van het account geeft; een weigering wegens het plan draagt needed en pricing_url.

Via de MCP-server is een nieuw pakket standaard 30 dagen geldig en hoogstens 90, ook na verlengen; een pakket dat nooit verloopt kan daar niet (expiry_not_allowed). Dat geldt voor elke sleutel; de REST-API hierboven houdt zijn eigen regels.

search_tools zoekt in alle tools van de site met dezelfde zoeker als /tools en geeft per tool de naam, een korte uitleg, de categorie, de formaten en de link naar de toolpagina (in het Nederlands, Engels of Frans). De tools zelf draaien in de browser van de gebruiker: de bestanden blijven op zijn computer, en de MCP-server bewerkt geen bestanden.

Geen gereedschap van de MCP-server vraagt het uploads-wachtwoord van het account, net als de REST-API: de sleutel met het recht maken is de machtiging en kan dus ook pakketten wijzigen, intrekken en verwijderen (zoals update_transfer, revoke_transfer en delete_transfer) en folders vullen. Geef een sleutel alleen lezen als dat niet mag.

get_received geeft per ontvangen bestand van een eigen opvraaglink een downloadlink die een uur werkt; zo'n link telt als een download door de eigenaar van het account, een keer per bestand per uur.

Zonder sleutel: een MCP-client die OAuth kent, vindt de autorisatieserver via de kop WWW-Authenticate van een 401 en /.well-known/oauth-protected-resource/mcp. De klant logt in en kiest de rechten (lezen staat aan, maken uit, mailen kan via OAuth niet). PKCE met S256 en de parameter resource (https://downloadlink.nl/mcp) zijn verplicht; registreren kan met een Client ID Metadata Document of via /oauth/register.

Een toegangstoken geldt een uur en alleen voor /mcp; een vernieuwtoken 30 dagen en werkt een keer (rotatie). De klant trekt een koppeling in op zijn accountpagina onder Gekoppelde assistenten.

3. Webhooks

Registreer een https-URL op je accountpagina (de knop "Test" stuurt direct een ping-event om je integratie te controleren). Zodra iemand je transfer downloadt, sturen we een POST met dit 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": "Offerte Q3",
    "url": "https://downloadlink.nl/p/a1b2c3d4e5f6a7b8",
    "download_type": "zip",
    "downloaded_at": "2026-06-17T10:30:00+00:00"
  }
}

Handtekening verifiëren

Bereken HMAC-SHA256 over de ruwe request-body met je webhook-secret en vergelijk constant-time met de header. Negeer berichten die niet kloppen.

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);
}

Antwoord met HTTP 2xx om de levering te bevestigen. Na 15 mislukte pogingen schakelen we de webhook uit; een geslaagde test (knop op je accountpagina) zet 'm weer aan. Alleen publieke https-URL's zijn toegestaan.