API og MCP
Send dokumenter til underskrift med MitID, BankID eller tegnet underskrift direkte fra jeres egne systemer, eller lad en AI-assistent som Claude gøre det for jer.
API'et og MCP-serveren kan det samme som Forsegl-appen. De bruger de samme regler, rettigheder og den samme API-nøgle, så det, I bygger med det ene, virker også med det andet.
| Tjeneste | Adresse |
|---|---|
| REST API | https://app.forsegl.dk/api/v1 |
| MCP-server | https://app.forsegl.dk/mcp (Streamable HTTP) |
| OpenAPI 3.1 | https://app.forsegl.dk/api/v1/openapi.json |
API-nøgler
Opret en nøgle i appen under API og MCP. Nøglen starter med fsg_ og vises kun én gang, så gem den et sikkert sted, f.eks. i en password manager eller jeres secrets-håndtering.
Send nøglen i Authorization-headeren på hver forespørgsel:
Authorization: Bearer fsg_…
- En nøgle handler på vegne af den bruger, der oprettede den. Sager, den opretter, får brugeren som afsender.
- Nøglen har adgang til hele organisationens sager, mapper, brands og webhooks.
- Nøglen kan ikke ændre adgangskoder eller oprette og tilbagekalde andre nøgler. Det kræver, at man er logget ind i appen.
- Tilbagekald en nøgle i appen, så holder den op med at virke med det samme. Brug én nøgle pr. integration eller person.
Hurtigstart
Fire kald fra kladde til sendt sag. Underskriverne får en mail med et personligt link.
# 1. Opret en sag
curl https://app.forsegl.dk/api/v1/cases \
-H "Authorization: Bearer $FORSEGL_KEY" -H "Content-Type: application/json" \
-d '{"title": "Lejekontrakt, Vesterbrogade 14"}'
# → {"id": "6f1c…", "reference": "SG-00042", "status": "draft", …}
# 2. Tilføj en PDF (base64 eller en offentlig https-adresse)
curl https://app.forsegl.dk/api/v1/cases/6f1c…/documents \
-H "Authorization: Bearer $FORSEGL_KEY" -H "Content-Type: application/json" \
-d '{"documents": [{"name": "Lejekontrakt.pdf", "url": "https://example.com/lejekontrakt.pdf"}]}'
# 3. Tilføj underskrivere
curl -X PUT https://app.forsegl.dk/api/v1/cases/6f1c…/signers \
-H "Authorization: Bearer $FORSEGL_KEY" -H "Content-Type: application/json" \
-d '{"signers": [{"name": "Anna Holm", "email": "anna@example.com"},
{"name": "Bo Lund", "email": "bo@example.com", "role": "copy"}]}'
# 4. Send til underskrift
curl -X POST https://app.forsegl.dk/api/v1/cases/6f1c…/send \
-H "Authorization: Bearer $FORSEGL_KEY"
Følg sagen med GET /cases/{id} eller en webhook. Når status er completed, henter I den underskrevne PDF med GET /cases/{id}/documents/{documentId}/file og signaturbeviset med GET /cases/{id}/certificate.pdf.
Forbind en AI-assistent (MCP)
Forsegl har en MCP-server (Model Context Protocol), så AI-assistenter kan arbejde direkte i Forsegl: oprette sager, tilføje dokumenter og underskrivere, sende, minde om, følge status og hente underskrevne dokumenter. Assistenten bruger jeres API-nøgle og kan det samme som API'et.
Claude Code
claude mcp add --transport http forsegl https://app.forsegl.dk/mcp \
--header "Authorization: Bearer fsg_…"
Claude Desktop
Åbn Indstillinger → Udvikler → Rediger konfiguration, tilføj følgende, og genstart Claude. Kræver Node.js.
{
"mcpServers": {
"forsegl": {
"command": "npx",
"args": ["-y", "mcp-remote", "https://app.forsegl.dk/mcp",
"--header", "Authorization: Bearer fsg_…"]
}
}
}
Cursor, VS Code, Windsurf og andre
Klienter, der understøtter MCP over HTTP med egne headere:
{
"mcpServers": {
"forsegl": {
"type": "http",
"url": "https://app.forsegl.dk/mcp",
"headers": { "Authorization": "Bearer fsg_…" }
}
}
}
send_case og remind_signers. Tjek modtagerne, før du godkender.MCP-værktøjer
Hvert værktøj svarer til et eller flere API-kald. Svarene er JSON, og PDF'er returneres som filer, assistenten kan gemme eller læse.
| Område | Værktøjer |
|---|---|
| Sager | list_cases get_case create_case update_case duplicate_case delete_case |
| Forløb | send_case remind_signers cancel_case extend_deadline set_access_period refresh_case_status |
| Dokumenter | add_documents add_sample_document rename_document reorder_documents remove_document download_document download_certificate get_evidence |
| Underskrivere | set_signers revoke_signer_link renew_signer_link |
| Mapper | list_folders create_folder update_folder delete_folder |
| Brands | list_brands get_brand create_brand update_brand set_default_brand delete_brand upload_brand_image remove_brand_image set_brand_domain verify_brand_domain remove_brand_domain |
| Webhooks | list_webhooks create_webhook update_webhook delete_webhook test_webhook |
| Konto | whoami get_stats list_activity list_contacts list_emails |
create_case kan gøre alt i ét trin: titel, besked, frist, dokumenter, underskrivere og eventuelt afsendelse.
Eksempler på prompts
- "Send vedhæftede lejekontrakt til underskrift hos Anna Holm (anna@example.com) med frist på fredag."
- "Hvilke sager venter stadig på underskrift, og hvem mangler?"
- "Send en påmindelse til alle, der ikke har underskrevet aftalerne i mappen Ansættelser."
- "Hent de underskrevne dokumenter og signaturbeviset for SG-00042."
- "Lav et nyt brand til datterselskabet med vores grønne farve, og brug det på mappen Nordic."
Konventioner
- Alle forespørgsler og svar er JSON (
Content-Type: application/json), undtagen fil-downloads. - Datoer er ISO 8601 i UTC, f.eks.
2026-12-01T22:59:00.000Z. - Id'er er UUID'er. Sagsnummeret (
reference, f.eks.SG-00042) er til mennesker og kan bruges i søgning. - Ændringer returnerer den opdaterede sag, så I sjældent behøver at hente den igen.
- En sag er en kladde (
draft), indtil den sendes. Dokumenter, underskrivere og de fleste felter kan kun ændres i en kladde.
Sagens status
| Status | Betydning |
|---|---|
draft | Kladde, ikke sendt |
pending | Sendt og afventer underskrifter |
completed | Alle har underskrevet. Underskrevne PDF'er og signaturbevis kan hentes |
rejected | En underskriver har afvist |
expired | Fristen er overskredet. Kan forlænges |
cancelled | Annulleret af afsender |
Sager
| Endpoint | Beskrivelse |
|---|---|
| GET/cases | Liste. Filtre: status (all, draft, pending, completed, rejected, expired, cancelled, action), folder, q |
| POST/cases | Opret kladde: title, folderId, language |
| GET/cases/{id} | Sag med dokumenter, underskrivere (inkl. personlige links) og hændelseslog |
| PATCH/cases/{id} | title, message, language, sequential, expiresAt, reminderDays, folderId, brandId. Efter afsendelse kun title, folderId og reminderDays |
| DELETE/cases/{id} | Slet en sag, der ikke er i gang |
| POST/cases/{id}/send | Send til underskrift |
| POST/cases/{id}/remind | Påmind alle, der mangler, eller én: {"signerId": "…"} |
| POST/cases/{id}/cancel | Annullér en igangværende sag |
| POST/cases/{id}/extend | Ny frist: {"expiresAt": "…"} |
| POST/cases/{id}/access | Hvor længe underskrivere kan åbne de underskrevne dokumenter: {"accessUntil": "…" | null} |
| POST/cases/{id}/duplicate | Kopiér til ny kladde |
| POST/cases/{id}/sync | Hent seneste status fra eID-udbyderen |
| GET/cases/{id}/certificate.pdf | Signaturbevis (fuldførte sager) |
| GET/cases/{id}/evidence | Rå eID-bevis pr. underskriver: ID-token signeret af eID-udbyderen (med nøglen) eller BankID-signaturen, og dokument-bindingen. nonce = base64url(SHA-256(binding)), så beviset kan efterprøves uafhængigt af Forsegl. Indeholder personoplysninger. |
| GET/cases/{id}/archive.zip | Alle dokumenter og signaturbeviset som ZIP |
Dokumenter
| Endpoint | Beskrivelse |
|---|---|
| POST/cases/{id}/documents | Tilføj PDF'er (højst 20 pr. kald, 25 MB pr. fil) |
| POST/cases/{id}/documents/sample | Tilføj et eksempeldokument til test |
| PATCH/cases/{id}/documents/{docId} | Omdøb: {"name": "…"} |
| PUT/cases/{id}/documents/order | Rækkefølge: {"ids": ["…", "…"]} |
| DELETE/cases/{id}/documents/{docId} | Fjern fra kladde |
| GET/cases/{id}/documents/{docId}/file | PDF'en. version=signed (standard) eller original, download=1 for vedhæftning |
Dokumenter kan sendes som JSON med base64 eller en offentlig https-adresse, som Forsegl henter, eller som multipart/form-data i feltet files:
{
"documents": [
{ "name": "Kontrakt.pdf", "contentBase64": "JVBERi0xLjcK…" },
{ "name": "Bilag 1.pdf", "url": "https://example.com/bilag-1.pdf" }
]
}
Svaret indeholder sagen samt added (nye dokument-id'er) og rejected (navne på filer, der ikke var gyldige PDF'er eller ikke kunne hentes).
Underskrivere
| Endpoint | Beskrivelse |
|---|---|
| PUT/cases/{id}/signers | Erstat listen af underskrivere og kopimodtagere i en kladde |
| POST/cases/{id}/signers/{signerId}/revoke | Spær et link, f.eks. hvis mailen gik til en forkert |
| POST/cases/{id}/signers/{signerId}/renew | Udsted et nyt link og send det: {"notify": true} |
{
"signers": [
{ "name": "Anna Holm", "email": "anna@example.com", "title": "Direktør", "authMethod": "mitid" },
{ "name": "Erik Berg", "email": "erik@example.se", "language": "en", "authMethod": "bankid_se" },
{ "name": "Revisor", "email": "revisor@example.com", "role": "copy" }
]
}
| Felt | Beskrivelse |
|---|---|
role | signer (standard) underskriver, copy modtager kun de underskrevne dokumenter |
authMethod | mitid, mitid_business, bankid_se, bankid_no eller drawn (tegnet underskrift). Udelades den, bruges brandets standard |
language | da eller en til mails og underskriftsside. Standard er sagens sprog |
id | Medtag id for at beholde en eksisterende underskriver, når listen erstattes |
Med "sequential": true på sagen underskriver de i listens rækkefølge, og den næste får først mail, når den forrige har underskrevet.
Mapper og brands
| Endpoint | Beskrivelse |
|---|---|
| GET/folders | Mapper med antal sager |
| POST/folders | Opret: name, color, brandId |
| PATCH/folders/{id} | Ret navn, farve eller brand |
| DELETE/folders/{id} | Slet mappen. Sagerne bevares |
| GET/brands | Brand-profiler |
| POST/brands | Opret: name, evt. copyFrom |
| GET/brands/{id} | Profil med alle indstillinger: farver, skrifttype, tekster, mailskabeloner, underskriftsforløb |
| PATCH/brands/{id} | name og/eller hele settings-objektet |
| POST/brands/{id}/default | Gør til organisationens standard |
| DELETE/brands/{id} | Slet (ikke standarden) |
| POST/brands/{id}/assets/{kind} | Billede (logo, logoDark, icon, cover): {"contentBase64": "…"} |
| PUT/brands/{id}/domain | Eget domæne til underskriftslinks: {"domain": "sign.firma.dk"} |
| POST/brands/{id}/domain/verify | Kontrollér DNS og aktivér domænet |
Konto
| Endpoint | Beskrivelse |
|---|---|
| GET/me | Brugeren og organisationen bag nøglen |
| GET/stats | Antal sager pr. status, fuldført denne måned, gennemsnitlig tid til underskrift |
| GET/activity | Seneste hændelser på tværs af sager (limit op til 100) |
| GET/contacts | Tidligere underskrivere (q til søgning) |
| GET/outbox | Mails sendt af Forsegl med leveringsstatus |
Fejl og grænser
Fejl returnerer en HTTP-statuskode og en maskinlæsbar kode:
{ "error": "not_draft" }
| Status | Eksempler |
|---|---|
| 400 | validation (med issues), not_draft, no_documents, no_signers, not_completed |
| 401 | unauthorized, invalid_api_key |
| 403 | session_required (kun muligt i appen) |
| 404 | not_found: findes ikke i jeres organisation |
| 413 | file_too_large |
| 429 | too_many_requests, se headeren Retry-After |
Grænser: 600 API-kald og 300 MCP-kald i minuttet pr. nøgle. 25 MB pr. PDF, 20 dokumenter pr. kald og 50 underskrivere pr. sag.
Webhooks
Tilføj en https-adresse under API og MCP i appen eller via API'et, så sender Forsegl en POST med JSON, når der sker noget i en sag.
| Endpoint | Beskrivelse |
|---|---|
| GET/webhooks | Endpoints med seneste leveringsresultat |
| POST/webhooks | Opret: {"url": "https://…", "events": ["case.completed"]}. Svaret indeholder secret |
| GET/webhooks/{id} | Ét endpoint inkl. secret |
| PATCH/webhooks/{id} | Ret url eller events |
| DELETE/webhooks/{id} | Fjern |
| POST/webhooks/{id}/test | Send en ping-hændelse |
| GET/webhooks/events | Hændelser, der kan abonneres på |
Svar med en 2xx-status inden for 10 sekunder. Fejler leveringen, prøver Forsegl igen efter 30 sekunder, 5 minutter og 30 minutter. Den samme hændelse kan i sjældne tilfælde komme mere end én gang, så brug id til at se bort fra dubletter.
Hændelser
| Hændelse | Hvornår |
|---|---|
case.sent | Sagen er sendt til underskrift |
signer.opened | En underskriver har åbnet sit link |
signer.signed | En underskriver har underskrevet |
signer.rejected | En underskriver har afvist |
case.completed | Alle har underskrevet, og dokumenterne er klar |
case.cancelled | Sagen er annulleret |
case.expired | Fristen er overskredet |
case.extended | Fristen er forlænget |
signer.reminded | Der er sendt en påmindelse |
signer.link_revoked, signer.link_renewed | Et link er spærret eller fornyet |
{
"id": "evt_3b0d6f8e-…",
"type": "case.completed",
"createdAt": "2026-11-05T09:41:12.004Z",
"data": {
"case": { "id": "6f1c…", "reference": "SG-00042", "title": "Lejekontrakt, Vesterbrogade 14",
"status": "completed", "completedAt": "2026-11-05T09:41:11.870Z" },
"signer": null
}
}
Verificér signaturen
Hver levering har headeren Forsegl-Signature: t=<unix-tid>,v1=<hex>. v1 er HMAC-SHA256 af "{t}.{rå body}" med webhookens hemmelighed (whsec_…). Afvis forespørgsler, hvor signaturen ikke passer, eller hvor t er mere end 5 minutter gammel.
import crypto from 'node:crypto';
export function verifyForsegl(rawBody, header, secret) {
const { t, v1 } = Object.fromEntries(header.split(',').map((p) => p.split('=')));
if (Math.abs(Date.now() / 1000 - Number(t)) > 300) return false;
const expected = crypto.createHmac('sha256', secret).update(`${t}.${rawBody}`).digest('hex');
return crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(v1));
}
import hmac, hashlib, time
def verify_forsegl(raw_body: bytes, header: str, secret: str) -> bool:
parts = dict(p.split("=", 1) for p in header.split(","))
if abs(time.time() - int(parts["t"])) > 300:
return False
expected = hmac.new(secret.encode(), f'{parts["t"]}.'.encode() + raw_body, hashlib.sha256).hexdigest()
return hmac.compare_digest(expected, parts["v1"])
Sikkerhed
- Al trafik går over HTTPS. Data og dokumenter opbevares hos Akamai (Linode) i Frankfurt, Tyskland.
- API-nøgler gemmes kun som hash. Mister I en nøgle, så tilbagekald den og opret en ny.
- Dokument-adresser og webhook-adresser skal være offentlige https-adresser. Interne netværk afvises.
- Personlige underskriftslinks i API-svar giver adgang til at underskrive. Behandl dem som fortrolige.
- Alt, der sker i en sag, også via API og MCP, logges i sagens hændelseslog og signaturbeviset.
Spørgsmål til integrationen? Skriv til info@wecode.dk.