Blog
Business1 August 2026 10 min🇩🇰 Denmark

API integration for accounting firms

Learn how your accounting firm integrates LegalDock's API and starts generating legal documents programmatically. A guide with curl and TypeScript.

Karoline, Dokumentkonsulent

Written for Danish law and Danish contract practice.

Modern accounting firms are digitalising more and more. Clients expect fast answers, and internal systems, from bookkeeping software to client portals, need to fit together better. Legal documents are the next link in the digital chain.

LegalDock's API lets accounting firms generate, fill in and send legal documents programmatically, directly from your own system. No manual copying, no outdated Word templates and no printed signatures.

This guide shows you how to integrate the LegalDock API.

What can the LegalDock API do?

LegalDock's REST API gives access to:

Function Description
Document generation Fill a template with fields and receive a finished PDF
Digital signature Send the document for signature via MitID
Webhooks Receive events when the document is signed, rejected or expires
Template list Retrieve all available templates and field descriptions
Document archive Retrieve, download and manage created documents

All calls go over HTTPS. Authentication uses API keys (a Bearer token). Rate limits depend on your plan.

Prerequisites

Before you begin:

  1. A LegalDock Partner account with API access
  2. An API key, retrieved under Settings, API, Generate key
  3. An HTTP client, curl for testing and Node.js 18+ for the TypeScript examples

Step 1: verify the connection

curl https://api.legaldock.org/v1/ping \
  -H "Authorization: Bearer YOUR_API_KEY"

Expected response:

{ "status": "ok", "version": "1.0" }

A 401 Unauthorized error means the API key is invalid or expired. Generate a new one under Settings.

Step 2: retrieve available templates

curl https://api.legaldock.org/v1/templates \
  -H "Authorization: Bearer YOUR_API_KEY"
{
  "templates": [
    { "id": "ansaettelseskontrakt", "name": "Ansaettelseskontrakt" },
    { "id": "nda-standard", "name": "NDA Fortrolighedsaftale" },
    { "id": "lejekontrakt-erhverv", "name": "Erhvervslejekontrakt" },
    { "id": "aktionaeroverenskomst", "name": "Aktionaeroverenskomst" },
    { "id": "konsulentaftale", "name": "Konsulentaftale" }
  ]
}

Inspect a template to see the field list:

curl https://api.legaldock.org/v1/templates/nda-standard \
  -H "Authorization: Bearer YOUR_API_KEY"

Step 3: generate a document

curl -X POST https://api.legaldock.org/v1/documents \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "templateId": "nda-standard",
    "fields": {
      "part1Navn": "TechStart ApS",
      "part2Navn": "Udvikler A/S",
      "formaal": "Droeftelse af muligt teknologisamarbejde",
      "varighed": "2 aar",
      "dato": "2026-06-01"
    },
    "meta": {
      "reference": "klient-456-nda-2026"
    }
  }'

Response:

{
  "documentId": "doc_xyz789",
  "status": "draft",
  "downloadUrl": "https://api.legaldock.org/v1/documents/doc_xyz789/pdf",
  "previewUrl": "https://app.legaldock.org/preview/doc_xyz789"
}

Tip: Use meta.reference to store your internal client or case number. It lets you find the document from your own system and link the API response to the right client file.

Step 4: request a digital signature

curl -X POST https://api.legaldock.org/v1/documents/doc_xyz789/sign \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "signers": [
      { "name": "Klaus Jensen", "email": "kj@techstart.dk", "role": "party1" },
      { "name": "Sara Holm", "email": "sh@udvikler-as.dk", "role": "party2" }
    ],
    "deadline": "2026-06-15",
    "notifyOnSigned": "webhook",
    "webhookUrl": "https://your-system.dk/webhooks/legaldock"
  }'

LegalDock sends an email to both parties with a signing link. Both sign via MitID on desktop or mobile.

TypeScript integration

An example of a reusable helper for Node.js and TypeScript:

// src/legaldock.ts
const BASE = 'https://api.legaldock.org/v1';

interface CreateDocumentPayload {
  templateId: string;
  fields: Record<string, string | number>;
  reference: string;
}

interface Signer {
  name: string;
  email: string;
  role: string;
}

export async function createDocument(
  apiKey: string,
  payload: CreateDocumentPayload
): Promise<{ documentId: string; downloadUrl: string }> {
  const res = await fetch(`${BASE}/documents`, {
    method: 'POST',
    headers: {
      Authorization: `Bearer ${apiKey}`,
      'Content-Type': 'application/json',
    },
    body: JSON.stringify({
      templateId: payload.templateId,
      fields: payload.fields,
      meta: { reference: payload.reference },
    }),
  });

  if (!res.ok) {
    const { message } = await res.json() as { message: string };
    throw new Error(`LegalDock createDocument: ${message}`);
  }

  return res.json() as Promise<{ documentId: string; downloadUrl: string }>;
}

export async function requestSignature(
  apiKey: string,
  documentId: string,
  signers: Signer[],
  webhookUrl: string,
  deadline?: string
): Promise<{ signingSessionId: string }> {
  const res = await fetch(`${BASE}/documents/${documentId}/sign`, {
    method: 'POST',
    headers: {
      Authorization: `Bearer ${apiKey}`,
      'Content-Type': 'application/json',
    },
    body: JSON.stringify({
      signers,
      deadline,
      notifyOnSigned: 'webhook',
      webhookUrl,
    }),
  });

  if (!res.ok) {
    const { message } = await res.json() as { message: string };
    throw new Error(`LegalDock requestSignature: ${message}`);
  }

  return res.json() as Promise<{ signingSessionId: string }>;
}

Integrate into your existing codebase:

import { createDocument, requestSignature } from './src/legaldock';

const API_KEY = process.env.LEGALDOCK_API_KEY!;
const WEBHOOK = process.env.LEGALDOCK_WEBHOOK_URL!;

// Called when the accountant onboards a new client employee
export async function issueEmploymentContract(
  employeeName: string,
  employeeEmail: string,
  employerName: string,
  employerEmail: string,
  startDate: string,
  monthlySalary: number,
  clientRef: string
) {
  const doc = await createDocument(API_KEY, {
    templateId: 'ansaettelseskontrakt',
    fields: {
      medarbejdernavn: employeeName,
      virksomhedsnavn: employerName,
      startdato: startDate,
      maanedsloen: monthlySalary,
      arbejdssted: 'Se separat bilag',
    },
    reference: clientRef,
  });

  await requestSignature(
    API_KEY,
    doc.documentId,
    [
      { name: employeeName, email: employeeEmail, role: 'employee' },
      { name: employerName, email: employerEmail, role: 'employer' },
    ],
    WEBHOOK,
    '2026-07-01'
  );

  return doc.documentId;
}

Webhooks: receive events automatically

Avoid manual polling, and use webhooks to update your system when documents are signed.

// Express webhook handler
import express from 'express';
import crypto from 'crypto';

const router = express.Router();

router.post('/webhooks/legaldock', express.json(), (req, res) => {
  const sig = req.headers['x-legaldock-signature'] as string;
  const expected = crypto
    .createHmac('sha256', process.env.LEGALDOCK_WEBHOOK_SECRET!)
    .update(JSON.stringify(req.body))
    .digest('hex');

  if (sig !== expected) {
    return res.status(401).json({ error: 'Invalid signature' });
  }

  const { event, documentId, reference } = req.body as {
    event: string;
    documentId: string;
    reference: string;
  };

  if (event === 'document.signed') {
    // Update the client file, send confirmation, archive the PDF
    console.log(`Signed: ${documentId} (client ref: ${reference})`);
  }

  res.json({ ok: true });
});

export default router;

Best practices for accounting firms

Use a sandbox for testing. If LegalDock offers a sandbox environment, use it during integration so test documents do not count against your quota and do not send real emails.

Always store the PDF locally. Download and archive the PDF file in your own system immediately after signing. Use the downloadUrl from the API response.

Set a deadline on signature requests. An NDA that is not signed in time creates confusion. Use the deadline field to manage expectations.

Monitor the error rate. Log all API calls and monitor 4xx and 5xx responses. A sudden rise in 429 errors (rate limit) signals that you need to upgrade the plan or implement backoff.

Error handling: reference table

HTTP code Cause Solution
400 Invalid fields Check field names against the template specification
401 Invalid API key Generate a new key under Settings
403 Plan restriction Upgrade your plan
404 Resource not found Check the templateId or documentId
429 Rate limit Implement exponential backoff
500 Server error Contact LegalDock support

Ready to integrate?

Create a partner account with API access, generate your API key under the settings, and read the full API reference in LegalDock's documentation before you start. If you need help, contact partner support.


This guide is technical guidance and does not constitute legal advice. Contact a lawyer for advice on the documents you generate.

This article is for general guidance only and is not individual legal advice. LegalDock documents are templates — consult a lawyer about your specific situation.