Kortlinks og hemmeligheder direkte fra koden
Én bearer-nøgle, almindelig JSON og de samme planregler som i dashboardet. Lavet til browserudvidelser, CLI'er, MCP-servere og interne værktøjer.
Tidlig adgang. Vi slår v1-endpoints til ét arbejdsområde ad gangen. Hvis et kald returnerer 503 med koden disabled, er API'et ikke slået til for dig endnu. Du kan oprette nøgler allerede i dag, og de virker, så snart det er.
Klientbiblioteker
Hemmeligheder krypteres, før de sendes, så hver klient skal selv klare den del. Disse biblioteker gør det én gang og korrekt, testet mod de samme testvektorer i repositoriet, som webappen bruger. Alle tre er MIT-licenserede, udgives fra CI med npm provenance og afhænger kun af standardbiblioteket.
TypeScript-SDK
npmTypet klient til alle ruter. Ingen afhængigheder, kun WebCrypto, så den kører uændret på Node 20+, Deno, Bun, browsere og edge-runtimes.
npm i @uselinkpilot/sdkKommandolinje
npmSend hemmeligheden ind via en pipe i stedet for som argument: et argument havner i din shell-historik og kan ses i proceslisten.
npm i -g linkpilot
echo 'db password: hunter2' | linkpilot secret --ttl 1hMCP-server
npmLader en AI-assistent give nogen loginoplysninger uden at lægge dem i en chatlog. Tilføj dette til Claude Desktop, Claude Code, Cursor eller en anden MCP-klient:
{
"mcpServers": {
"linkpilot": {
"command": "npx",
"args": ["-y", "@uselinkpilot/mcp"],
"env": { "LINKPILOT_API_KEY": "lp_live_..." }
}
}
}Godt at vide, før du installerer den: klarteksten passerer gennem assistentens kontekst, fordi modellen skal se en hemmelighed for at kunne oprette den. Serveren beskytter hemmeligheden under overførsel og i lagring, og linket kan kun åbnes én gang. Hemmeligheden kan ikke skjules for selve modellen.
Kildekode: linkpilot-cli (SDK og CLI) og linkpilot-mcp.
1. Autentificering
Opret en nøgle på /app/api-keys (ejere og administratorer af arbejdsområdet). Den rå nøgle vises kun én gang, og LinkPilot gemmer kun dens SHA-256-hash. Send den med hver forespørgsel:
Authorization: Bearer lp_live_...Nøgler er knyttet til et arbejdsområde, så der er ingen tenant-parameter nogen steder. Tilbagekaldte, udløbne, ukendte og forkert formaterede nøgler returnerer alle det samme 401 unauthorized. Nøgler skrives aldrig i logfiler.
Basis-URL: https://khiydxamyihofmtmgvjz.supabase.co/functions/v1/api-v1
2. Ruter
| Metode | Sti | Hvad den gør |
|---|---|---|
| GET | /me | Arbejdsområde, plan, grænser, forbrug og din nøgles forespørgselskvote |
| POST | /links | Opret et kortlink (URL samt valgfri slug, domæne, titel og tags) |
| GET | /links | Vis links, nyeste først, med cursor-paginering |
| GET | /links/{id} | Hent ét link |
| DELETE | /links/{id} | Slet et link |
| POST | /secrets | Opret et hemmeligt link (ciphertext, enc_version, ttl_seconds, burn_after_read og passphrase_hash på Pro) |
| GET | /secrets | Vis metadata for hemmeligheder (aldrig indholdet) |
| DELETE | /secrets/{id} | Tilbagekald en hemmelighed, og destruer indholdet |
| POST | /public/links | Opret et anonymt link med et client_id, uden nøgle |
| POST | /public/secrets | Opret en anonym hemmelighed, der slettes efter læsning, uden nøgle |
Lister returnerer { data, next_cursor }. Send next_cursor tilbage som cursor for at hente flere. limit er 1 til 100.
3. Eksempler
Opret et link med curl
curl -X POST "https://khiydxamyihofmtmgvjz.supabase.co/functions/v1/api-v1/links" \
-H "Authorization: Bearer $LINKPILOT_API_KEY" \
-H "Content-Type: application/json" \
-d '{"url":"https://example.com/launch","slug":"launch","tags":["campaign"]}'Opret en hemmelighed fra JavaScript
const b64u = (b) => btoa(String.fromCharCode(...new Uint8Array(b)))
.replace(/+/g, "-").replace(///g, "_").replace(/=+$/, "");
// 1. Encrypt locally. This key is generated here and never sent anywhere.
const key = crypto.getRandomValues(new Uint8Array(32));
const iv = crypto.getRandomValues(new Uint8Array(12));
const k = await crypto.subtle.importKey("raw", key, "AES-GCM", false, ["encrypt"]);
const ct = await crypto.subtle.encrypt(
{ name: "AES-GCM", iv, tagLength: 128 },
k,
new TextEncoder().encode("staging db password"),
);
const ciphertext = `v1.${b64u(iv)}.${b64u(ct)}`;
// 2. Store the blob. LinkPilot cannot read it.
const res = await fetch("https://khiydxamyihofmtmgvjz.supabase.co/functions/v1/api-v1/secrets", {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.LINKPILOT_API_KEY}`,
"Content-Type": "application/json",
},
body: JSON.stringify({ ciphertext, enc_version: 1, ttl_seconds: 3600 }),
});
if (!res.ok) throw new Error((await res.json()).error.code);
const { secret_url } = await res.json();
// 3. Attach the key. Without this the link can never be opened, by anyone.
const shareUrl = `${secret_url}#k=${b64u(key)}`;Vis links fra Python
import os, requests
r = requests.get(
"https://khiydxamyihofmtmgvjz.supabase.co/functions/v1/api-v1/links",
params={"limit": 20},
headers={"Authorization": f"Bearer {os.environ['LINKPILOT_API_KEY']}"},
)
r.raise_for_status()
for link in r.json()["data"]:
print(link["short_url"], "->", link["url"])
print("remaining this hour:", r.headers["X-RateLimit-Remaining"])Opret et anonymt link uden nøgle
curl -X POST "https://khiydxamyihofmtmgvjz.supabase.co/functions/v1/api-v1/public/links" \
-H "Content-Type: application/json" \
-d '{"url":"https://example.com","client_id":"my-cli-0123456789abcdef"}'Den formelle kontrakt udgives her og ikke i et repository: OpenAPI 3.1-dokumentet, Postman-samlingen og den skrevne reference. Peg din kodegenerator direkte på OpenAPI-URL'en.
4. Sådan gemmes hemmeligheder
Vi vil gerne være præcise her, fordi en API-klient selv har reelt arbejde at gøre: API'et accepterer slet ikke et payload i klartekst.
- Du krypterer, vi gemmer en blob. Send
ciphertext(AES-GCM-256,v1.<iv>.<ct>) medenc_version: 1. Sender dupayload, bliver kaldet afvist i stedet for at blive accepteret i stilhed. - Du skal selv tilføje nøglen. Den
secret_url, vi returnerer, indeholder ingen nøgle og kan heller ikke gøre det: vi har aldrig set nøglen. Tilføj#k=<base64url key>. Browsere sender aldrig den del af linket, der står efter#, så modtageren dekrypterer lokalt. - Der er ingen gendannelse. Mister du nøglen, er hemmeligheden væk, både for dig og for os. Sådan virker garantien.
- Destrueres ved læsning, udløb eller tilbagekaldelse. Den gemte chiffertekst overskrives på serveren.
- API'et returnerer den aldrig. Ingen rute sender indhold eller chiffertekst tilbage. Det eneste, der læser den, er modtagerens visningsside, og den registrerer visningen.
- Adgangssætninger (Pro og højere) er en reel anden faktor: de strækkes med PBKDF2-SHA256 og indregnes i nøglen med HKDF, så hverken linket eller adgangssætningen alene kan dekryptere. Send kun
passphrase_hash, dvs. SHA-256-hex af den NFKC-normaliserede adgangssætning. Send aldrig selve adgangssætningen, for den er halvdelen af nøglen. Afled nøglen af den samme NFKC-form, så en adgangssætning, der er skrevet på et andet tastatur, stadig åbner hemmeligheden. - Hvad det ikke dækker. Den, der har hele linket, har også nøglen, så kort udløbstid og sletning efter læsning er stadig vigtige. Vedhæftede filer krypteres på samme måde med en nøgle, der er afledt af det samme link.
Dataformatet er beskrevet i API-referencen, og @uselinkpilot/sdk klarer hele udvekslingen i ét kald, hvis du hellere vil slippe for at skrive det selv. Samme model er beskrevet på siden om sikkerhedsarkitektur.
5. Plangrænser og forespørgselsgrænser
| Grænse | Free | Pro | Agency |
|---|---|---|---|
| Links | 50 | Ubegrænset | Ubegrænset |
| Aktive hemmeligheder | 5 | 100 | 100 |
| Hemmeligheder med adgangssætning | Nej | Ja | Ja |
| Egne domæner til links | Eksisterende verificerede domæner | Op til 5 | Ubegrænset |
| Forespørgsler pr. nøgle pr. time | 60 | 1.000 | 5.000 |
API'et bruger de samme planregler som dashboardet og henter dem samme sted fra, så et bureaus samlede grænser på tværs af arbejdsområder gælder også her. GET /me returnerer de aktuelle tal for dit arbejdsområde. Alle autentificerede svar indeholder X-RateLimit-Limit, X-RateLimit-Remaining og X-RateLimit-Reset.
6. Offentlige ruter uden nøgle
POST /public/links og POST /public/secrets findes til klienter, der ikke kan vise en browserudfordring: CLI'er, MCP-servere og launcheren. De tager et opakt client_id (16 til 64 tegn, genereres én gang pr. installation) og opretter poster i den samme anonyme pulje som de gratis webværktøjer.
- 20 oprettelser pr. klient pr. dag og 60 pr. netværk pr. time, plus puljens egne grænser pr. netværk.
- Den samme screening som alle andre måder at oprette på: trusselsfeeds, Googles opslag af farlige URL'er og planlagte genkontroller.
- Intet eget domæne, ingen egen slug og ingen adgangssætning. Offentlige hemmeligheder slettes altid efter én læsning og udløber inden for 24 timer.
- Svaret indeholder et
claim_token, så en bruger, der er logget ind, senere kan knytte posten til et arbejdsområde. Behandl det som loginoplysninger.
7. Fejl
Alle fejl har formen { "error": { "code", "message", "upgrade_url"? } }. Meddelelserne nævner feltet, aldrig dine data. Ressourcer i andre arbejdsområder giver 404, ikke 403.
| Kode | HTTP | Hvornår |
|---|---|---|
| unauthorized | 401 | Manglende, forkert formateret, ukendt, tilbagekaldt eller udløbet nøgle |
| disabled | 503 | API'et er endnu ikke slået til for denne installation |
| invalid_request | 400 | Valideringsfejl, blokeret destination, en slug, der allerede er i brug, eller et ukendt domæne |
| not_found | 404 | Ruten findes ikke, eller ressourcen er ikke i dit arbejdsområde |
| plan_limit | 402 | Kvoten for links eller aktive hemmeligheder er nået (upgrade_url medfølger) |
| pro_required | 402 | Adgangssætning på en plan, der ikke har den (upgrade_url medfølger) |
| rate_limited | 429 | Nøglens timekvote eller den offentlige kvote er brugt op (Retry-After medfølger) |
Spørgsmål fra udviklere
- Hvilke planer giver adgang til API'et?
- API-nøgler oprettes fra dashboardet på /app/api-keys. Hver nøgle følger plangrænserne for sit arbejdsområde: Free har 50 links og 5 aktive hemmeligheder med 60 forespørgsler i timen. Pro har ubegrænsede links, 100 aktive hemmeligheder, hemmeligheder beskyttet med adgangssætning og 1.000 forespørgsler i timen. Agency-planerne får 5.000 forespørgsler i timen.
- Er indholdet af hemmeligheder end-to-end-krypteret?
- Ja, og API'et accepterer slet ikke klartekst. Du krypterer med AES-GCM-256 og sender chiffertekst. LinkPilot gemmer en blob, som vi ikke har nøglen til og ikke kan dekryptere. Da vi aldrig ser din nøgle, indeholder den secret_url, vi returnerer, ingen nøgle. Du tilføjer den selv som et #k=-fragment, og den del sender browsere aldrig. Mister du nøglen, kan ingen gendanne hemmeligheden, heller ikke vi. Vedhæftede filer krypteres på samme måde med en nøgle, der er afledt af det samme link.
- Kan jeg oprette links uden en API-nøgle?
- Ja. POST /public/links og POST /public/secrets tager et opakt client_id i stedet for en nøgle og opretter poster i den samme anonyme pulje som de gratis webværktøjer: 20 oprettelser pr. klient pr. dag, 60 pr. netværk pr. time, intet eget domæne og ingen adgangssætning. Hemmeligheder udløber inden for 24 timer.
- Hvad sker der, når jeg rammer en grænse?
- Plangrænser returnerer HTTP 402 med koden plan_limit eller pro_required og en upgrade_url. Forespørgselsgrænser returnerer 429 med en Retry-After-header. Alle autentificerede svar indeholder X-RateLimit-Limit, X-RateLimit-Remaining og X-RateLimit-Reset, så klienter kan sætte tempoet ned, før det sker.
- Er API'et stabilt?
- v1-ruterne ændres ikke på en måde, der bryder eksisterende integrationer. Den slags ændringer kommer i en ny version. I perioden med tidlig adgang slås endpoints til ét arbejdsområde ad gangen. Et 503-svar med koden disabled betyder, at API'et endnu ikke er slået til for dit arbejdsområde.
Klar til at bygge?
Opret en nøgle i dit arbejdsområde, eller start med Free-planen.