Misi.Link

API-dokumentasjon

Send filer fra dine egne verktøy (skript, programmer, servere): opprett en overføring, last opp filene i deler, få lenken.

Nøkler følger med abonnementene Pro og Bedrift, under Min konto › API. Priser

Autentisering

Opprett en nøkkel under Min konto › API, og send den i Authorization-headeren i hver forespørsel. Hold den hemmelig: den gir tilgang til overføringene dine. En slettet nøkkel slutter å virke med en gang.

Authorization: Bearer mlk_…

Hvis verktøyet eller hostingen din ikke sender videre Authorization-headeren, send den samme nøkkelen i headeren X-API-Key:

X-API-Key: mlk_…

120 forespørsler per minutt per nøkkel. Størrelse, antall filer og tilgjengelighet: som i abonnementet ditt.

Send filer i tre trinn

1. Opprett overføringen

Oppgi listen over filer (navn og størrelse i byte). Med «recipients» får hver mottaker en e-post; uten mottakere får du en lenke å dele. Valgfritt: «message», «password», «expire_days», «delete_after_download».

curl -X POST https://misi.link/api/v1/transfers \
  -H "Authorization: Bearer $MISILINK_KEY" -H "Content-Type: application/json" \
  -d '{"files": [{"name": "film.mov", "size": 5368709120}],
       "recipients": ["client@example.com"], "message": "…", "expire_days": 14}'
{"ok": true, "id": "aB3dE5fG", "token": "…", "url": "https://misi.link/aB3dE5fG",
 "files": [{"id": 101, "name": "film.mov", "size": 5368709120}], "chunk_size": 8388608}

2. Last opp filene

Send hver fil i deler på høyst «chunk_size» byte, i rekkefølge: «offset» er antall byte som allerede er mottatt for filen. Etter et avbrudd viser «status» hvor du fortsetter (svar 409: fortsett ved «received»). Hvis webhotellet eller brannmuren filtrerer adresser, sender du posisjonen i headeren X-Upload-Offset i stedet for i adressen.

curl -X POST "https://misi.link/api/upload/chunk?id=aB3dE5fG&token=…&file=101&offset=0" \
  -H "Content-Type: application/octet-stream" --data-binary @morceau-1.bin
# {"ok": true, "received": 8388608, "done": false}

curl "https://misi.link/api/upload/status?id=aB3dE5fG&token=…"

3. Fullfør

Når alle filene er mottatt, fullfører du: overføringen publiseres og e-postene sendes. Lenken er den fra det første svaret («url»). Dette trinnet er obligatorisk. Deretter lagrer serveren filene: i mellomtiden er «status» «finalizing». En overføring som er mottatt i sin helhet, men aldri fullført, fullføres automatisk etter 30 minutter uten aktivitet.

curl -X POST https://misi.link/api/upload/complete -H "Content-Type: application/json" \
  -d '{"id": "aB3dE5fG", "token": "…"}'

Direkte opplasting til lagringen (anbefalt for store filer)

Med «direct»: true sender programmet ditt filen rett til misi.links lagring, uten å gå via serveren: maksimal hastighet og tilgjengelig på noen sekunder til slutt. Én fil per overføring (flere: samle dem i et arkiv). Uten dette valget, eller hvis svaret inneholder «direct»: null, laster du opp delene med «chunk» som over.

  1. Opprett overføringen med «direct»: true. Svaret oppgir «direct.part_size» (størrelsen på hver del, den siste er kortere), «direct.parts» (antall deler) og «direct.sign».
  2. Hent de signerte adressene fra «sign» (ID, token og delnumre, høyst 100 per kall; gyldige i 6 timer), og send hver del med en vanlig PUT til adressen sin, uten autentiseringsheader. Delene kan sendes parallelt og sendes på nytt etter en feil.
  3. Fullfør med «complete». Mangler en del, er svaret «incomplete» med listen «missing»: send de delene på nytt og kall «complete» igjen. Ende-til-ende-kryptering: hver del er en rekke hele krypterte segmenter, i rekkefølge (se eksempelet).
curl -X POST https://misi.link/api/v1/transfers -H "Authorization: Bearer $MISILINK_KEY" -H "Content-Type: application/json" \
  -d '{"files": [{"name": "film.mov", "size": 5368709120}], "direct": true}'
# {…, "direct": {"part_size": 16777216, "parts": 320, "total": 5368709120, "sign": "https://misi.link/api/upload/sign"}}

curl -X POST https://misi.link/api/upload/sign -H "Content-Type: application/json" \
  -d '{"id": "aB3dE5fG", "token": "…", "parts": [1, 2, 3]}'
# {"ok": true, "urls": {"1": "https://…", "2": "https://…", "3": "https://…"}}

curl -X PUT -H "Content-Type: application/octet-stream" --data-binary @part-1.bin "https://…"

curl -X POST https://misi.link/api/upload/complete -H "Content-Type: application/json" -d '{"id": "aB3dE5fG", "token": "…"}'
# {"ok": false, "error": "incomplete", "missing": [2]}

Ende-til-ende-kryptering (valgfritt)

Programmet ditt krypterer filen før den sendes: misi.link mottar og lagrer bare uleselige data. Nøkkelen sendes aldri til serveren: den står i lenken, etter «#». Mottakeren laster ned og dekrypterer i nettleseren, på den vanlige siden.

  1. Lag en tilfeldig nøkkel på 16 byte, og opprett overføringen med «e2e»: true og én enkelt fil (den ukrypterte størrelsen i byte). Flere filer samler du først i et arkiv.
  2. Krypteringsnøkkel: HKDF-SHA-256 av nøkkelen (salt «misilink-e2e-v1», info = overføringens ID, 16 byte), for AES-GCM.
  3. Del filen i segmenter på 4 MiB (4 194 304 byte). Segment nr. i: nonce = 4 nullbyte og deretter i på 8 byte (big-endian); tilknyttede data = i på 8 byte og deretter 1 byte (1 for det siste segmentet, ellers 0). Hvert kryptert segment er 16 byte lengre: last opp disse bytene med «chunk» i rekkefølge («e2e.encrypted_size» totalt), og fullfør med «complete».
  4. Anbefalt: «e2e_verify» = HKDF-SHA-256 av nøkkelen (samme salt, info «misilink-verify», 32 byte) i base64url. misi.link avviser da en nedlasting med feil nøkkel, uten noen gang å kjenne den riktige.
  5. Lenke å dele: «url» + «#» + nøkkelen i base64url (22 tegn). Med mottakere («recipients») legger du til «e2e_key» (nøkkelen): misi.link oppbevarer den forseglet for å sette den inn i lenkene i e-postene.
curl -X POST https://misi.link/api/v1/transfers \
  -H "Authorization: Bearer $MISILINK_KEY" -H "Content-Type: application/json" \
  -d '{"files": [{"name": "film.mov", "size": 5368709120}], "e2e": true, "e2e_verify": "…"}'
# {"ok": true, "id": "aB3dE5fG", …, "e2e": {"segment": 4194304, "tag": 16, "encrypted_size": 5368729600, "version": 1}}

Fullstendig eksempel i Python (biblioteket cryptography):

#!/usr/bin/env python3
# misi.link: end-to-end encrypted upload through the API (Python 3.8+, pip install cryptography)
# The file goes straight to the storage (signed URLs); if direct upload is unavailable, through "chunk".
# Usage: MISILINK_KEY=mlk_... python3 misilink_e2e.py file.mov [https://misi.link]
import base64, json, os, sys, urllib.request
from cryptography.hazmat.primitives.ciphers.aead import AESGCM
from cryptography.hazmat.primitives.hashes import SHA256
from cryptography.hazmat.primitives.kdf.hkdf import HKDF

SEG, SALT = 4 * 1024 * 1024, b'misilink-e2e-v1'
b64 = lambda b: base64.urlsafe_b64encode(b).rstrip(b'=').decode()
hkdf = lambda ikm, info, n: HKDF(SHA256(), n, SALT, info).derive(ikm)


def call(url, body=None, raw=None, headers=None, method=None, auth=True):
    h = dict(headers or {})
    if auth:
        h['Authorization'] = 'Bearer ' + os.environ['MISILINK_KEY']
    data = raw if raw is not None else (json.dumps(body).encode() if body is not None else None)
    if data is not None:
        h.setdefault('Content-Type', 'application/octet-stream' if raw is not None else 'application/json')
    req = urllib.request.Request(url, data=data, headers=h, method=method or ('POST' if data is not None else 'GET'))
    with urllib.request.urlopen(req, timeout=600) as r:
        body = r.read()
        return json.loads(body) if body[:1] == b'{' else {}


def segments(path, key, size):
    n = max(1, -(-size // SEG))
    with open(path, 'rb') as f:
        for i in range(n):
            last = i == n - 1
            nonce = bytes(4) + i.to_bytes(8, 'big')
            aad = i.to_bytes(8, 'big') + (b'\x01' if last else b'\x00')
            yield key.encrypt(nonce, f.read(SEG), aad)        # SEG bytes in, SEG + 16 bytes out


def send(path, base='https://misi.link'):
    size = os.path.getsize(path)
    secret = os.urandom(16)                                   # the key: never sent to the server
    verify = b64(hkdf(secret, b'misilink-verify', 32))       # lets the server recognise the right key
    t = call(base + '/api/v1/transfers', {'files': [{'name': os.path.basename(path), 'size': size}],
                                         'e2e': True, 'e2e_verify': verify, 'direct': True})
    key = AESGCM(hkdf(secret, t['id'].encode(), 16))
    auth = {'id': t['id'], 'token': t['token']}
    d = t.get('direct')
    if d:                                                     # straight to the storage: parts of d['part_size'] bytes
        part, n, urls = b'', 0, {}
        def put(n, data):
            if str(n) not in urls:
                urls.update(call(d['sign'], {**auth, 'parts': list(range(n, min(n + 100, d['parts'] + 1)))})['urls'])
            call(urls[str(n)], raw=data, method='PUT', auth=False)
        for seg in segments(path, key, size):
            part += seg
            if len(part) == d['part_size']:
                n += 1; put(n, part); part = b''
        if part:
            n += 1; put(n, part)
    else:                                                     # through the server
        sent = 0
        for seg in segments(path, key, size):
            r = call(t['upload']['chunk'] + '?id=' + t['id'] + '&token=' + t['token'] + '&file=' + str(t['files'][0]['id']),
                     raw=seg, headers={'X-Upload-Offset': str(sent)})
            sent = r['received']
    call(t['upload']['complete'], auth)
    return t['url'] + '#' + b64(secret)                      # the key travels after "#", only in the link


if __name__ == '__main__':
    print(send(sys.argv[1], *(sys.argv[2:3] or [])))

Andre forespørsler

GET /api/v1/transfers
Overføringene dine, 50 per side (?page=2…).
GET /api/v1/transfers/{id}
Én overføring: status, lenke, filer, antall nedlastinger, utløpsdato.
DELETE /api/v1/transfers/{id}
Slett en overføring (lenken slutter å virke).
GET /api/v1/me
Kontoen din, abonnementet ditt og grensene, størrelsen på delene.

Datoer i ISO 8601-format (UTC), størrelser i byte.

Svar og feil

Alle svar er JSON med «ok». Ved feil: «error» (kort kode) og «message». HTTP-koder: 401 nøkkel mangler eller er ugyldig, 403 abonnement uten API, 404 ikke funnet, 409 del på feil sted, 422 forespørsel avvist, 429 for mange forespørsler (vent ett minutt).