Snabbstart
Base URL: https://tidvis.se/api/public/v1. Alla anrop använder JSON. Public API är en del av Enterprise-planen.
- 1. Skapa en API-klient under Sign → Inställningar → API och spara
client_idochclient_secret. - 2. Hämta en
access_tokenvia OAuth2 client credentials. - 3. Skapa ett avtal, ladda upp en PDF, lägg till deltagare och skicka.
curl -X POST https://tidvis.se/api/public/v1/oauth/access-token \
-H "Content-Type: application/json" \
-d '{"client_id":"tvc_...","client_secret":"tvs_..."}'En enda modell — API och plattform är samma avtal
Avtal skapade via API:t ligger i samma tabell och delar hela infrastrukturen med avtal skapade i webb-UI:t. Konkret betyder det:
- Syns direkt i plattformen under fliken Via integration i
/app/sign/dokument. - Går att öppna, patcha, påminna och avbryta både via API och i UI:t.
- Ingår i samma auto-reminder-cron, arkiv och 18-månaders retention som native-avtal.
- Deltagarnas signeringslänk är alltid
https://tidvis.se/sign/<token>— samma flöde som avtal skapade i plattformen. - UI:t visar en Via {external_source}-badge (klickbar när
external_urlär satt) så administratören kan hoppa tillbaka till CRM-posten. - Skickar du med
senderi requesten autosigneras avsändaren vidsend— samma beteende som när en användare skickar från plattformen.
Autentisering
OAuth2 client credentials. Du byter client_id+client_secret mot en JWT som är giltig i 1 timme. Skicka Authorization: Bearer <access_token> på alla efterföljande anrop.
/oauth/access-tokenHämta en access token (1h TTL).
{
"access_token": "eyJhbGciOiJIUzI1NiIs...",
"token_type": "Bearer",
"expires_in": 3600,
"expires_at": "2026-06-07T13:00:00.000Z"
}/oauth/access-tokenÅterkalla den aktuella token (kräver Authorization-header).
Avtal
/agreementsSkapa ett avtals-utkast (status: draft).
curl -X POST https://tidvis.se/api/public/v1/agreements \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{"title":"Anställningsavtal Anna","expires_in_days":14,"bankid_required":true,"sender":{"name":"Anna Andersson","email":"anna@tidvis.se"}}'sender (valfritt) — avsändaren autosigneras vid send (skapar en is_sender=true-rad) och får inget signeringsmail. Samma beteende som UI-flödet. Kan även sättas vid PATCH (medan draft) eller skickas direkt i lifecycle-anropet: {"action":"send","sender":{...}}.
Body-fält (renderas från OpenAPI-specen)
| Fält | Typ | Beskrivning |
|---|---|---|
titleobligatoriskt | string | — |
message | string | — |
expires_at | string (date-time) | — |
expires_in_days | integer | Alternativ till expires_at — sätter expires_at till nu + N dagar. expires_at vinner om båda skickas. |
callback_url | string (uri) | Äldre synonym till `webhook_url`. Används bara om `webhook_url` saknas. Nya integrationer ska använda `webhook_url`. |
webhook_url | string (uri) | Per-avtal webhook-URL (https). Sätts den skickas alla webhook-händelser för just detta avtal hit i stället för till klientens registrerade URL — använd när varje kundmiljö har sin egen mottagare (t.ex. https://kund.tidvis.se/dokument/signera/tidvissign/status/). Ingen förhandsregistrerad webhook krävs — adressen fungerar fristående och får alla händelsetyper för avtalet. Utskicket signeras med klientens signeringsnyckel (X-Tidvis-Signature). Alternativt kan händelser hämtas med GET /events.(kan vara null) |
metadata | object | — |
bankid_required | boolean | — |
bankid_identify_required | boolean | — |
document_source | "sender" | "recipient" | 'sender' (default) = avsändaren laddar upp PDF via PUT /documents/main. 'recipient' = mottagaren laddar upp PDF/PNG/JPG via publik dropzone; kräver exakt 1 recipient. |
external_id | string | — |
external_source | string | — |
external_url | string (uri) | — |
completed_delivery | "email_attachment" | "secure_link" | Leveranssätt för det signerade avtalet. `email_attachment` (standard): PDF bifogas i klart-mejlet. `secure_link`: ingen bilaga — mottagaren får en länk, identifierar sig med BankID och servern kontrollerar att personnumret tillhör en behörig mottagare innan en personlig nedladdning (giltig 10 min) skapas. Länken gäller 30 dagar efter att avtalet blev klart. Kräver BankID-tillvalet (402 `bankid_disabled`) och att varje signerare är knuten till ett personnummer: `bankid_required: true` eller `signer_personal_number` (annars 400 `personal_number_required_for_secure_delivery` vid utskick). Har kontot "Kräv alltid säker leverans" påslaget avvisas `email_attachment` med 409 `secure_delivery_enforced`. Utelämnas fältet gäller kontots standard. Ingen automatisk nedgradering till bilaga sker någonsin. Obs: BankID ensamt gör inte behandlingen GDPR-kompatibel — kunden ansvarar fortfarande för sin riskbedömning. |
completed_copy_recipients | array[object] (max 5) | Kopiemottagare som får hämta avtalet vid säker leverans. Personnumret används för behörighetskontrollen och lagras bara som hash. Vid `secure_link` får adresser i `completed_copy_emails` utan personnummer bara ett meddelande om att avtalet är klart — ingen länk. |
completed_copy_emails | array[email] (max 5) | Upp till 5 mejladresser som får bekräftelsemejlet med signerad PDF bifogad när avtalet blir completed. Utelämnas fältet ärvas kontots inställning. |
sender | object | Avsändare — autosigneras vid send (samma beteende som UI-flödet). Läggs till som is_sender=true och skickar inget signeringsmail. |
sender.name | string | — |
sender.emailobligatoriskt | string (email) | — |
auto_reminders | object | Automatic reminder settings (defaults: enabled=true, interval_days=3, max_reminders=3). Reminders run daily via cron; requires the account-level auto-reminder toggle to also be enabled. |
auto_reminders.enabled | boolean | — |
auto_reminders.interval_days | integer | — |
auto_reminders.max_reminders | integer | — |
/agreementsLista avtal. Stödjer ?status, ?limit, ?cursor.
/agreements/:idHämta ett avtal med deltagare och status. Detta är även status-polling-endpointen — se nedan.
Svarsfält — Agreement (renderas från OpenAPI-specen)
| Fält | Typ | Beskrivning |
|---|---|---|
id | string (uuid) | — |
status | "draft" | "sent" | "partially_signed" | "signed" | "completed" | "cancelled" | "declined" | "expired" | — |
title | string | — |
message | string | —(kan vara null) |
expires_at | string (date-time) | —(kan vara null) |
parties | array[object] | — |
external_id | string | —(kan vara null) |
external_source | string | —(kan vara null) |
external_url | string (uri) | —(kan vara null) |
metadata | object | —(kan vara null) |
version | integer | Versionsnummer i kedjan (1 = första). |
replaces_document_id | string (uuid) | Pekar på det avtal som denna version ersätter.(kan vara null) |
superseded_at | string (date-time) | Sätts när avtalet ersatts av en nyare version.(kan vara null) |
versions | array[object] | Hela versionskedjan (endast med i responsen när avtalet ingår i en kedja, dvs `version > 1` eller är superseded).(kan vara null) |
auto_reminders_enabled | boolean | — |
reminder_interval_days | integer | — |
max_reminders | integer | — |
webhook_url | string (uri) | Avtalets egen webhook-URL. null = klientens registrerade webhook-URL används (eller `callback_url` om den är satt på avtalet).(kan vara null) |
completed_copy_emails | array[email] (max 5) | Mejladresser som får en kopia av bekräftelsemejlet med den signerade PDF:en bifogad när avtalet blir completed. null = ärver kontots inställning (Inställningar i appen).(kan vara null) |
completed_delivery | "email_attachment" | "secure_link" | Leveranssätt satt på avtalet. null = kontots standard gäller.(kan vara null) |
signed_pdf_url | string (uri) | Short-lived signed URL to the stamped final PDF (only present after completion). Alltid null vid säker leverans (`secure_link`) — hämta då filen med API-nyckeln via `GET /agreements/{id}/signed-pdf`.(kan vara null) |
signed_at | string (date-time) | —(kan vara null) |
signed_sha256 | string | Hex SHA-256 of the stamped final PDF.(kan vara null) |
created_at | string (date-time) | — |
updated_at | string (date-time) | — |
/agreements/:id/eventsAudit-trail (sent, viewed, signed, completed...).
/agreements/:id/downloadHämta signerad PDF (returnerar signed URL eller binärt innehåll).
Kopia av signerade avtal (completed_copy_emails)
Skicka completed_copy_emails (upp till 5 adresser) när avtalet skapas via POST /agreements eller quick-send. Varje adress får bekräftelsemejlet med den signerade PDF:en bifogad när avtalet är klart. Utelämnas fältet (null) ärvas kontots inställning under Inställningar → Kopia av signerade avtal till. Fältet returneras på GET /agreements/:id — se fälttabellerna ovan.
Polla signeringsstatus
Tidvis Sign har ingen separat /status-resurs — GET /agreements/:id är pollingendpointen. Svaret innehåller avtalets status samt participants[].status och signed_at per part, så ni kan visa t.ex. "1 av 2 har signerat" i ert eget UI utan att vänta på webhook.
Rekommendation: använd webhooks (agreement.viewed, agreement.signed, agreement.completed) som primär kanal och fall tillbaka på polling med 30 s intervall när webhook inte är ett alternativ. Avtals-status kan vara draft, sent, partially_signed, completed, declined eller cancelled.
# Polla status och räkna signerade
curl -s https://tidvis.se/api/public/v1/agreements/$ID \
-H "Authorization: Bearer $TOKEN" \
| jq '{
status,
signed: ([.participants[] | select(.status == "signed")] | length),
total: (.participants | length),
participants: [.participants[] | {name, email, status, signed_at}]
}'Exempel-svar (1 av 2 signerade):
{
"status": "partially_signed",
"signed": 1,
"total": 2,
"participants": [
{ "name": "Anna Andersson", "email": "anna@example.com", "status": "signed", "signed_at": "2026-06-17T09:14:22Z" },
{ "name": "Erik Eriksson", "email": "erik@example.com", "status": "viewed", "signed_at": null }
]
}CRM-länkning (external_id)
Länka varje avtal till motsvarande objekt i ert CRM (deal, opportunity, ärende) genom att skicka external_id, external_source och external_url när avtalet skapas. Fälten returneras på GET /agreements/:id och bifogas i alla webhook-payloads (agreement.sent, agreement.signed, ...) så ni kan koppla tillbaka eventet till rätt CRM-post utan att slå upp via metadata.
Detta är grunden för att integrera Tidvis Sign med vilket CRM som helst – HubSpot, Upsales, Pipedrive, Salesforce, Lime, SuperOffice – via Zapier, Make, n8n eller egen kod.
curl -X POST https://tidvis.se/api/public/v1/agreements \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"title": "Anställningsavtal Anna",
"external_id": "deal_98231",
"external_source": "hubspot",
"external_url": "https://app.hubspot.com/contacts/123/deal/98231"
}'Webhook när avtalet signeras:
{
"event": "agreement.signed",
"delivery_id": "whd_...",
"created_at": "2026-06-20T12:00:00Z",
"agreement_id": "agr_...",
"external_id": "deal_98231",
"external_source": "hubspot",
"participant": { "id": "p_...", "email": "anna@example.com" },
"data": {
"agreement_id": "agr_...",
"external_id": "deal_98231",
"external_source": "hubspot",
"participant": { "id": "p_...", "email": "anna@example.com" }
}
}Låsa BankID till ett specifikt personnummer
Sätt signer_personal_number på en mottagare för att kräva att exakt den personen legitimerar sig och/eller signerar med BankID. Avvisas i BankID-appen om någon annan försöker. Kräver att avtalet har bankid_required: true och/eller bankid_identify_required: true. Fältet är write-only — det returneras aldrig i svar; använd personal_id_masked (t.ex. 19800101-XXXX) för visning.
{
"title": "Anställningsavtal Anna",
"bankid_required": true,
"participants": [
{
"name": "Anna Andersson",
"email": "anna@example.com",
"signer_personal_number": "198001011234"
}
]
}Accepterat format: 10 eller 12 siffror, valfritt bindestreck (t.ex. 198001011234, 800101-1234). Luhn-valideras. Skickas personnummer utan att BankID är aktiverat på avtalet, eller om numret inte klarar Luhn-kontrollen, returneras 400 invalid_request.
Versionering & ersättning
När ni skickar POST /agreements med samma external_source + external_id som ett tidigare avtal skapar vi automatiskt en ny version i kedjan. Den föregående versionen markeras som superseded_at och auto-cancelleras om den fortfarande var öppen. Signeringslänkar från den gamla versionen slutar fungera.
- Ärver send-läget: om föregående version var
sentkopieras signatärer + avsändare, och den nya versionen skickas automatiskt så fort PDF:en laddats upp (eller när första participant lagts till om PDF redan finns). - Listning:
GET /agreementsreturnerar per default bara aktuella versioner. Använd?group=allför att inkludera superseded. - Historik:
GET /agreements/:id/versionsger hela kedjan sorterad ASC.GET /agreements/:idinkluderarversions[]när avtalet ingår i en kedja. - Fält:
version(1..N),replaces_document_id,superseded_at.
# Skicka en uppdaterad version av samma HubSpot-deal
curl -X POST https://tidvis.se/api/public/v1/agreements \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"title": "Anställningsavtal Anna (v2 – uppdaterad lön)",
"external_id": "deal_98231",
"external_source": "hubspot"
}'
# Hämta hela kedjan
curl https://tidvis.se/api/public/v1/agreements/$NEW_ID/versions -H "Authorization: Bearer $TOKEN"Idempotency
Skicka headern Idempotency-Key: <unikt-värde> på POST /agreements för att säkert kunna retry:a vid timeout/nätfel utan att skapa dubletter. Vi cachar svaret i 24 timmar; identiska anrop returnerar samma resultat med headern Idempotent-Replayed: true. Anrop med samma key men annan body returnerar 409 conflict.
Rekommendation: använd ert CRM:s deal-id + ett datum, eller en UUID per logiskt avtal.
curl -X POST https://tidvis.se/api/public/v1/agreements \
-H "Authorization: Bearer $TOKEN" \
-H "Idempotency-Key: deal_98231-2026-06-12" \
-H "Content-Type: application/json" \
-d '{"title":"Anställningsavtal Anna","external_id":"deal_98231","external_source":"hubspot"}'Deltagare
/agreements/:id/participantsLägg till en signatär (utkast-status).
{
"name": "Anna Andersson",
"email": "anna@example.com",
"role": "signer",
"job_title": "VD",
"phone": "+46701234567",
"personal_id_masked": "19800101-XXXX",
"notifications_enabled": true
}/agreements/:id/participants/:pid/declineAvvisa avtalet för en specifik deltagare. Sätter avtalets status till 'declined'.
{ "reason": "Felaktiga villkor" }/agreements/:id/participants/:pid/delegateDelegera signeringen till en ny person. Befintlig deltagare markeras 'delegated'.
{
"name": "Erik Eriksson",
"email": "erik@example.com",
"job_title": "CFO",
"phone": "+46707654321",
"message": "Hänvisar till Erik som ansvarar för signering."
}/agreements/:id/participants/:pid/notificationsSlå av/på påminnelse-mejl för en enskild deltagare.
{ "enabled": false }/agreements/:id/chatLista chat-meddelanden mellan agenten och deltagarna på ett avtal.
/agreements/:id/chatSkicka ett chat-meddelande. Ange participant_id för att vidarebefordra ett meddelande från en deltagare. Triggar webhook agreement.chat_message.
{
"body": "Hej, kan ni granska bilaga 2?",
"sender_label": "Tidvis Sign Agent",
"participant_id": "8a3..."
}PDF-dokument
/agreements/:id/documents/mainLadda upp huvuddokument. Stöd för application/pdf (binärt) eller JSON med base64.
# JSON / base64
curl -X PUT https://tidvis.se/api/public/v1/agreements/$ID/documents/main \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{"filename":"avtal.pdf","content_base64":"JVBERi0..."}'
# Eller binärt
curl -X PUT https://tidvis.se/api/public/v1/agreements/$ID/documents/main \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/pdf" \
--data-binary @avtal.pdfLivscykel
/agreements/:id/lifecycleSkicka eller avbryt ett avtal.
{"action": "send"} // eller "cancel"OTP via e-post (standardmetod)
Alla avtal signeras som standard med e-post + engångskod (OTP). Ingen extra konfiguration krävs – när bankid_required är false eller utelämnas använder varje deltagare denna metod. Räknas som enkel elektronisk signatur (SES) enligt eIDAS.
Hur det fungerar
- Mottagaren får en personlig länk till sin e-postadress (samma som i
participants[].email). - En 6-siffrig engångskod skickas vid öppning av dokumentet, giltig i 15 minuter, max 5 försök.
- Identiteten bekräftas innan signaturen registreras; IP, user agent, tidpunkt och kodverifiering loggas i audit-trailen.
- Ingår på alla planer – ingen add-on eller styckkostnad.
Skapa OTP-avtal (default)
curl -X POST https://tidvis.se/api/public/v1/agreements \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"title": "Konsultavtal Anna",
"expires_in_days": 14
}'Webhook-payload vid OTP-signering
{
"event": "agreement.signed",
"delivery_id": "whd_...",
"created_at": "2026-06-11T08:42:11.000Z",
"agreement_id": "agr_...",
"participant": { "id": "p_...", "email": "anna@example.com" },
"signature": {
"method": "otp_email",
"signer_name": "Anna Andersson",
"email": "anna@example.com",
"code_verified_at": "2026-06-11T08:41:53.000Z",
"signed_at": "2026-06-11T08:42:11.000Z",
"ip": "203.0.113.42",
"user_agent": "Mozilla/5.0 ..."
}
}BankID
Alternativ till standardmetoden OTP via e-post när ni behöver avancerad elektronisk signatur (AES). Sätt bankid_required: true när du skapar avtalet (eller via PATCH innan det skickas). När avtalet skickas signerar varje deltagare med BankID på mobilen eller datorn istället för engångskod.
Krav
- Kontot måste ha planen Sign Pro och tillvalet BankID aktiverat.
- Pris: 100 BankID-signeringar ingår per månad, därefter 5 kr/signering enligt prislista.
- Saknar kontot tillvalet returnerar
POST /agreements/:id/lifecyclemedaction: "send"felet402 bankid_disabled.
Skapa BankID-avtal
curl -X POST https://tidvis.se/api/public/v1/agreements \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"title": "Anställningsavtal Anna",
"bankid_required": true
}'Svar (förkortat):
{
"id": "agr_...",
"status": "draft",
"bankid_required": true,
"document": { "uploaded": false, "sha256": null },
"participants": []
}Webhook-payload när BankID används
För avtal med bankid_required: true kompletteras agreement.signed-händelsen med ett signature-block. Av integritetsskäl exponeras varken personnummer, certifikat eller OCSP-svar via API:t eller webhooks.
{
"event": "agreement.signed",
"delivery_id": "whd_...",
"created_at": "2026-06-11T08:42:11.000Z",
"agreement_id": "agr_...",
"participant": { "id": "p_...", "email": "anna@example.com" },
"signature": {
"method": "bankid",
"signer_name": "Anna Andersson",
"signed_at": "2026-06-11T08:42:11.000Z"
}
}Fältet signature.method är alltid en av "otp_email" eller "bankid". Avtal med blandade deltagare loggar metoden per signatär.
BankID-identifiering innan öppning
Separat tillval som tvingar mottagaren att legitimera sig med BankID innan dokumentet visas. Detta är en gate framför själva PDF:en och är oberoende av vald signaturmetod — du kan alltså kräva BankID-legitimering och sedan låta signering ske med OTP via e-post, klick-signatur eller penn-signatur. Kombineras gärna med bankid_required: true när du vill ha både identifiering före öppning och BankID-signering vid undertecknande.
Slå på vid skapande
curl -X POST https://tidvis.se/api/public/v1/agreements \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"title": "NDA Anna",
"bankid_identify_required": true
}'Svar (förkortat):
{
"id": "agr_...",
"status": "draft",
"bankid_required": false,
"bankid_identify_required": true,
"participants": []
}Beteende för mottagaren
- När mottagaren öppnar länken visas en BankID-gate istället för PDF:en.
- Mottagaren måste starta BankID på mobil eller via QR-kod och bekräfta legitimering.
- Först därefter öppnas PDF:en och vald signaturmetod blir aktiv.
- "viewed"-händelsen loggas efter legitimeringen, inte vid första klicket på länken.
Krav och kostnad
- Kontot måste ha Sign Pro och tillvalet BankID aktiverat — samma krav som
bankid_required. - Identifieringar räknas inte mot BankID-kvoten på 100 signeringar/mån. Endast faktiska BankID-signeringar (
bankid_required: true) debiteras 5 kr/styck efter inkluderade. - Saknar kontot tillvalet returnerar
POST /agreements/:id/lifecyclemedaction: "send"felet402 bankid_disabled.
Webhook-payload
En extra händelse agreement.identified sänds när en mottagare legitimerat sig. Personnummer, certifikat och OCSP-svar exponeras aldrig via API:t.
{
"event": "agreement.identified",
"delivery_id": "whd_...",
"created_at": "2026-06-12T10:21:04.000Z",
"agreement_id": "agr_...",
"participant": { "id": "p_...", "email": "anna@example.com" },
"identification": {
"method": "bankid",
"signer_name": "Anna Andersson",
"identified_at": "2026-06-12T10:21:04.000Z"
}
}Avtalsgenerator
Bygg en AI-driven avtalsmall från 2–10 exempelavtal och generera sedan nya avtal med bara ett anrop. Genererade avtal kan laddas ner som DOCX ellerskickas direkt till signering via Sign — i så fall får du tillbaka ett sign_document_id.
Krav & pris
- Avtalsmodulen måste vara aktiv på kontot (99 kr/användare/månad). Annars returneras
402 plan_required. - Vid MCP/agent-anrop tillkommer 3 kr per generering ovanpå modulpriset.
- Källdokument max 20 MB per fil, 2–10 filer per mall (PDF eller DOCX).
Scopes
templates:read— lista och hämta mallartemplates:manage— skapa och radera mallargenerations:create— generera nya avtal från mallgenerations:read— lista, hämta och ladda ner genererade avtal- Om
signature_method: "bankid"används igeneratekrävs ävenagreements:send.
/agreement-templatesLista sparade avtalsmallar.
/agreement-templatesSkapa mall från 2–10 exempelavtal. Multipart (files[]) eller JSON med base64 (sources[]).
curl -X POST https://tidvis.se/api/public/v1/agreement-templates \
-H "Authorization: Bearer $TOKEN" \
-F "name=Konsultavtal" \
-F "files=@konsult1.pdf" \
-F "files=@konsult2.pdf" \
-F "files=@konsult3.docx"/agreement-templates/:idHämta ett mallschema (fält, block, källor).
/agreement-templates/:idRadera en mall (permanent).
/agreement-templates/:id/generateGenerera nytt avtal från mall. Returnerar DOCX inline (default) eller ett skickat Sign-utkast om send_for_signing=true och recipients anges.
{
"values": {
"kund_namn": "Acme AB",
"belopp": 45000,
"startdatum": "2026-08-01"
},
"send_for_signing": true,
"signature_method": "bankid",
"recipients": [
{ "name": "Anna Andersson", "email": "anna@acme.se" }
]
}/agreement-generations?template_id=...Lista genererade avtal (filtrera valfritt på template_id).
/agreement-generations/:idHämta metadata om en generering.
/agreement-generations/:id/downloadSignerad nedladdnings-URL (giltig 10 min) till genererad PDF/DOCX.
Fullständiga request/response-scheman finns i OpenAPI-referensen.
Underkonton (multi-tenant)
Hantera underkonton (dotterbolag, avdelningar, franchises) under huvudkontot. Kräver en aktiv årslicens (1 188 kr/år per underkonto, lookup-key sub_account_license_yearly) som aktiveras i Tidvis Sign → Underkonton. Alla endpoints kräver att din token tillhör huvudkontot, inte ett underkonto.
Scopes
sub_accounts:read— läs lista och detaljersub_accounts:manage— skapa, uppdatera, arkivera, ändra parent-accesssub_accounts:members— hantera medlemmar och roller
Scopes är opt-in per token — gå till Inställningar → API för att tilldela dem.
/sub-accountsLista underkonton. ?include_archived=true tar med arkiverade.
/sub-accountsSkapa nytt underkonto. Kräver aktiv årslicens.
curl -X POST https://tidvis.se/api/public/v1/sub-accounts \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"name": "Stockholmskontoret",
"org_number": "5560000000",
"inherit_parent_admins": true
}'/sub-accounts/:idHämta ett underkonto.
/sub-accounts/:idUppdatera namn eller org.nummer.
/sub-accounts/:idArkivera (soft-delete). Hård radering sker 30 dagar senare via daglig GDPR-purge.
/sub-accounts/:id/parent-accessSlå av/på automatisk åtkomst för huvudkontots ägare/admins.
{ "inherit_parent_admins": false }/sub-accounts/:id/membersLista medlemmar på underkontot.
/sub-accounts/:id/membersBjud in ny medlem via e-post.
{ "email": "anna@example.com", "role": "admin" }/sub-accounts/:id/members/:memberIdÄndra medlemsroll (owner/admin/member). Sista ägaren kan inte degraderas.
/sub-accounts/:id/members/:memberIdTa bort medlem. Sista ägaren kan inte tas bort.
Felkoder (specifika för underkonton)
403 license_required— ingen aktiv årslicens på huvudkontot403 license_seat_limit— alla seats på licensen är upptagna403 plan_limit— planen tillåter inte fler underkonton403 parent_only— token tillhör ett underkonto; använd huvudkontots token404 not_found— underkontot finns inte eller tillhör annat huvudkonto409 conflict— underkontot är arkiverat
GDPR / retention
Arkiverade underkonton raderas permanent efter 30 dagar via ett dagligt jobb (purge_archived_sub_accounts()) som tar bort kontot, medlemmar, inbjudningar samt tillhörande dokument i storage. Alla skrivningar via API:t loggas i sign_account_hierarchy_audit med actor_api_client_id så ni kan spåra exakt vilken token som utförde åtgärden.
CRM (read-only)
Underkonton exponeras inte via CRM-API:t som skrivbara entiteter — CRM:t är read-only enligt befintlig spec. Använd /v1/sub-accounts för all hantering.
Webhooks
Tidvis levererar händelser till din URL via POST med JSON. Varje request är signerad med HMAC-SHA256 i headern X-Tidvis-Signature: sha256=<hex> baserat på din signing_secret och raw body. Eventnamnet finns både i X-Tidvis-Event och i JSON-bodyts event/event_type. Vi retryar misslyckade leveranser med exponentiell backoff i upp till 24h.
Vill du att olika avtal ska rapporteras till olika mottagare (t.ex. en URL per kundmiljö) skickar du webhook_url när avtalet skapas — då levereras alla händelser för det avtalet dit i stället för till den registrerade URL:en. Ingen förhandsregistrerad webhook behövs — adressen fungerar fristående och får alla händelser för avtalet, signerade med klientens signeringsnyckel. Alternativt hämtar du händelser med GET /events.
/webhooksRegistrera en webhook.
{
"client_id": "<api_client uuid>",
"url": "https://din-app.se/webhooks/tidvis",
"events": ["agreement.sent","agreement.signed","agreement.completed"]
}/webhooksLista registrerade webhooks.
/webhooks/:idTa bort en webhook.
Händelser
agreement.sentagreement.viewedagreement.identifiedagreement.signedagreement.completedagreement.cancelledagreement.declinedagreement.delegatedparticipant.notifications_changedagreement.chat_messageagreement.comment_createdagreement.comment_resolvedagreement.expiredagreement.pdf_replacedagreement.superseded*
För avtal med bankid_required: true innehåller agreement.signed/agreement.completed även ett signature-block. Se BankID.
Varje leverans har ett event_id i bodyn. Samma händelse har alltid samma event_id — använd det för att filtrera bort upprepningar. agreement.completed innehåller dessutom participants och document.signed_pdf_url (hämta PDF:en med din API-nyckel).
Verifiera signatur (Node.js)
import { createHmac, timingSafeEqual } from "node:crypto";
function verify(rawBody, header, secret) {
const expected = createHmac("sha256", secret).update(rawBody).digest("hex");
const given = (header || "").replace(/^sha256=/, "");
if (given.length !== expected.length) return false;
return timingSafeEqual(Buffer.from(expected), Buffer.from(given));
}Felkoder
| HTTP | Code | Betydelse |
|---|---|---|
| 400 | invalid_request | Felaktig input. |
| 401 | unauthorized | Saknad/ogiltig token. |
| 402 | plan_required | Kontot saknar Enterprise-plan. Innehåller upgrade_url. |
| 402 | bankid_disabled | Avtalet har bankid_required: true men kontot saknar BankID-tillvalet. |
| 403 | forbidden | Saknar rättighet till resursen. |
| 404 | not_found | Resursen hittades inte. |
| 409 | conflict | Ogiltigt tillståndsbyte (t.ex. skicka redan skickat). |
| 429 | rate_limited | För många anrop. |
| 500 | server_error | Serverfel – försök igen. |
Rate limits
Standardgräns: 60 requests/minut per klient och 600/timme för uppladdningar. Vid överskridning får du 429 rate_limited med Retry-After-header. Behöver du mer? Kontakta oss.
MCP & AI-agenter
Tidvis Sign exponerar en Model Context Protocol-server så AI-agenter (ChatGPT, Claude Desktop, egna agenter) kan skapa, skicka och följa avtal med ett enkelt verktygsanrop. Discovery-manifest finns på /.well-known/mcp.json.
Endpoint
POST https://tidvis.se/api/mcp
Authorization: Bearer <access_token>
Content-Type: application/json
Accept: application/json, text/event-streamScopes
API-tokens kan begränsas till specifika scopes. För MCP krävs mcp:connect + valfri kombination av:
agreements:read– hämta avtal & eventsagreements:create– skapa utkast & deltagareagreements:send– skicka avtal till signeringagreements:cancel– avbryt avtalbilling:checkout– generera checkout-länktemplates:read/templates:manage– hantera avtalsmallargenerations:create/generations:read– generera & läsa avtal från mallmcp:connect– krävs för MCP-åtkomst
Tillgängliga verktyg
tidvis_sign_create_agreementtidvis_sign_add_participanttidvis_sign_upload_pdftidvis_sign_send_agreementtidvis_sign_cancel_agreementtidvis_sign_get_agreementtidvis_sign_list_eventstidvis_sign_create_checkouttidvis_sign_decline_agreementtidvis_sign_delegate_signingtidvis_sign_set_participant_notificationstidvis_sign_list_chat_messagestidvis_sign_post_chat_messagetidvis_sign_list_agreement_templatestidvis_sign_get_agreement_templatetidvis_sign_create_agreement_templatetidvis_sign_generate_agreementtidvis_sign_list_agreement_generationstidvis_sign_get_agreement_generation
Claude Desktop-konfiguration
{
"mcpServers": {
"tidvis-sign": {
"url": "https://tidvis.se/api/mcp",
"headers": { "Authorization": "Bearer YOUR_ACCESS_TOKEN" }
}
}
}Agent-checkout & pay-per-agreement
Två sätt för en agent att låta slutkunden betala:
- Sign Pro (prenumeration) – månadsavgift per säte, obegränsat antal avtal. Använd
tidvis_sign_create_checkoutmedproduct: "sign_pro_monthly"och valfritt antal seats. - Pay-per-agreement – 19 SEK/avtal som förbetalda krediter, perfekt för agenter med låg volym. Använd
product: "sign_per_agreement"och valfritt antal krediter (1–500). Krediter dras automatiskt närsend_agreementkörs på free-plan-konton.
Checkout-verktyget returnerar en Stripe-hostad URL som agenten visar för slutkunden. Vid 402 plan_required på send_agreement är konto utan krediter och utan Sign Pro – svaret innehåller upgrade_url som agenten kan länka till.