Dokflow External API v1
Dokflow External API er et server-til-server API for å opprette Dokflow-forespørsler og e-CMR fra eksterne systemer, som ERP-, CRM- og fagsystemer eller egne apper.
Kom i gang på 5 minutter
- Gå til Innstillinger → Stripe Connect → HTTP API i Dokflow.
- Opprett en API-nøkkel, for eksempel
Produksjonsintegrasjon. - Gi nøkkelen bare rettighetene integrasjonen trenger.
- Kopier nøkkelen med én gang. Den vises bare én gang.
- Kall
GET /api/external/v1/for å kontrollere nøkkelen og se hvilke endepunkter organisasjonen faktisk har tilgang til. - Hent deretter maler med
GET /templates.php, finn ønskettemplate_idog feltetskey, og opprett forespørselen medPOST /requests.php.
Viktig: e-CMR-endepunktet og e-CMR-scopes er bare tilgjengelige når e-CMR-modulen er aktivert for organisasjonen. Hvis modulen er deaktivert, annonseres ikke e-CMR i discovery-responsen og API-nøkkelen kan ikke få e-CMR-rettigheter.
Base og autentisering
Base:
/api/external/v1
Send API-nøkkelen på alle kall:
Authorization: Bearer dfk_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
Content-Type: application/json
X-Dokflow-API-Key støttes også, men
Authorization: Bearer anbefales.
API-nøkler opprettes av administrator under Innstillinger → Stripe Connect → HTTP API. Klartekstnøkkelen vises bare én gang og skal kun lagres på serversiden. Ikke bygg nøkkelen inn i nettleser-JavaScript eller en distribuert mobilapp.
Alle svar inneholder request_id, og samme verdi sendes i
HTTP-headeren X-Request-Id.
Test API-nøkkelen
GET /api/external/v1/
Returnerer organisasjon, nøkkelnavn, scopes og bare de V1-endepunktene som faktisk er tilgjengelige for denne organisasjonen og API-nøkkelen. Dette er anbefalt discovery-kall ved oppstart av en integrasjon.
Scopes
Tilgjengelige scopes avhenger også av hvilke Dokflow-moduler organisasjonen har aktivert. e-CMR-scopes vises ikke og kan ikke tildeles dersom e-CMR-modulen ikke er aktiv.
templates:read– hente aktive maler og feltskjemarequests:read– hente status på forespørslerrequests:write– opprette forespørslerecmr:read– hente e-CMR/statusecmr:write– opprette e-CMR
Første testkall
cURL
curl -sS \
-H "Authorization: Bearer dfk_DIN_NOKKEL" \
-H "Accept: application/json" \
https://dokflow.no/api/external/v1/PHP
<?php
$apiKey = getenv('DOKFLOW_API_KEY');
$ch = curl_init('https://dokflow.no/api/external/v1/');
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => [
'Authorization: Bearer ' . $apiKey,
'Accept: application/json',
],
]);
$response = curl_exec($ch);
$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
$data = json_decode((string) $response, true);Bruk alltid HTTPS i produksjon og hold API-nøkkelen på serveren.
Maler
Hent alle aktive maler
GET /api/external/v1/templates.php
Hent én mal
GET /api/external/v1/templates.php?id=12
Responsen inneholder malens felt og key for hvert felt.
Felt med api_writable: true kan sendes i
fields ved opprettelse. Dette er felter som i Dokflow er
satt til Dere ved opprettelse (låst). Filfelt lastes
ikke opp gjennom JSON-endepunktet i V1.
For flerpartssignering returneres også signing.roles.
Bruk role_key fra denne listen når signatarer sendes
inn.
Feltmapping
Et eksternt system trenger ikke vite hvordan malen ser ut visuelt.
Integrasjonen bruker feltenes stabile key.
Eksempel fra GET /templates.php?id=12:
{
"key": "ordrenummer",
"label": "Ordrenummer",
"type": "text",
"required": true,
"filled_by": "sender",
"api_writable": true
}Send deretter verdien slik:
{
"fields": {
"ordrenummer": "84291"
}
}Bare felter med api_writable: true skal fylles fra
API-et. Felt mottakeren skal fylle ut selv, signaturfelt og vanlige
filopplastinger sendes ikke som fritekst gjennom
fields.
Opprett Dokflow-forespørsel
POST /api/external/v1/requests.php
Eksempel:
{
"external_id": "system:case:84291:contract",
"template_id": 12,
"title": "Avtale – ordre 84291",
"recipient": {
"name": "Ola Nordmann",
"phone": "+4791234567",
"email": "ola@example.no"
},
"fields": {
"ordrenummer": "84291",
"arbeidsadresse": {
"street": "Eksempelveien 1",
"postal_code": "5003",
"city": "Bergen",
"country": "Norge"
},
"avtalt_belop": 12500
},
"message": "Se gjennom og signer dokumentet.",
"delivery": {
"send_sms": true,
"send_email": false
}
}Ved vellykket opprettelse returneres blant annet Dokflow-ID, offentlig URL, status og eventuell leveringsstatus.
Flerpartssignering
Når malen har flere signaturroller:
{
"external_id": "system:case:84291:handover",
"template_id": 18,
"fields": {
"ordrenummer": "84291"
},
"signing_mode": "parallel",
"participants": [
{
"role_key": "kunde",
"name": "Ola Nordmann",
"phone": "+4791234567",
"email": "ola@example.no"
},
{
"role_key": "montor",
"name": "Kari Montør",
"phone": "+4798765432"
}
],
"delivery": {
"send_participant_sms": true,
"send_participant_email": false
}
}role_key må være en rolle som returneres fra
mal-endepunktet.
SMS OTP og signaturintegritet
Et signaturfelt kan ha signature_sms_otp: true. Innstillingen styres per signaturfelt i malen og returneres av GET /templates.php. Når OTP er aktivert må den aktuelle signataren ha et gyldig telefonnummer. For enkeltsignering brukes recipient.phone (eller et telefonfelt som er mappet i malen). For flerpartssignering må den aktuelle posten i participants ha phone, med mindre rollen får telefon fra et senderutfylt felt.
Etter hver godkjente SES-signatur lager Dokflow et uforanderlig evidence-snapshot, beregner SHA-256 og sender kun hashen til OpenTimestamps. Ved flerpartssignering opprettes et eget anchor etter hvert signeringssteg. GET /requests.php returnerer disse i request.signature_anchors, blant annet med trigger_signature_id, signature_count, evidence_sha256, proof_sha256, calendar, state og submitted_at. Hvis en offentlig OTS-kalender er midlertidig utilgjengelig beholdes snapshotet, og Dokflow forsøker eldre ventende anchors igjen ved et senere vellykket signeringssteg. state: submitted betyr at kalenderen har mottatt commitmentet og at .ots-proofet er lagret; Bitcoin-attestasjonen blir tilgjengelig senere gjennom normal OpenTimestamps-oppgradering.
Betalingsfelt, produktlinjer, MVA og kvittering
Hvis malen inneholder betaling, returnerer mal-endepunktet
betalingsfeltets konfigurasjon. payment_line_items_mode kan
være:
single– én betalingslinje, som før. Beløpet kan være fast, angis ved opprettelse eller hentes fra et senderutfylt beløpsfelt.fixed– flere faste produktlinjer er definert direkte i malen. Integrasjonen skal ikke sende linjene eller en separat totalsum; Dokflow bruker de lagrede produktlinjene og beregner totalen.repeat– produktlinjene hentes fra en gjentakende tabell som fylles ut av virksomheten (filled_by: sender). Integrasjonen sender tabellradene ifields.
For fixed returneres payment_line_items.
For repeat returneres
payment_repeat_field_key,
payment_repeat_description_column_key, valgfri
payment_repeat_quantity_column_key,
payment_repeat_unit_amount_column_key og valgfri
payment_repeat_vat_column_key.
line_items_source_field beskriver tabellfeltet, inkludert
kolonnene og om feltet er API-skrivbart.
Et leverings-/ytelsessted kan være et felt som fylles ut ved
opprettelse (filled_by: sender) eller av
mottakeren (filled_by: recipient).
Mottakerutfylte verdier trenger ikke sendes i API-kallet; Dokflow
kontrollerer og fryser den faktiske verdien når kunden starter
betalingen. Hvis kildesystemet skal sende verdien på forhånd, må malen
bruke et sender-felt.
For betalingsmaler må opprettelsen i tillegg inneholde
receipt_buyer. Kjøper må ha navn og enten
organisasjonsnummer eller full adresse. email brukes til
automatisk utsending av kvitteringslenken når betalingen er
captured.
Én betalingslinje med beløp angitt ved opprettelse
Hvis betalingsfeltet bruker single og beløpskilden er
Angis ved opprettelse, send beløpet i
payment_fields. Bruk betalingsfeltets key fra
mal-endepunktet:
{
"template_id": 22,
"external_id": "system:case:84291:deposit",
"fields": {
"ordrenummer": "84291"
},
"payment_fields": {
"depositum": {
"amount": 2500
}
},
"recipient": {
"name": "Ola Nordmann",
"phone": "+4791234567",
"email": "ola@example.no"
},
"receipt_buyer": {
"name": "Eksempelbedriften AS",
"organization_number": "999999999",
"vat_number": "999999999",
"address": "Eksempelveien 1",
"postal_code": "5003",
"city": "Bergen",
"country": "Norge",
"email": "regnskap@example.no"
},
"delivery": {
"send_sms": true
}
}Beløp i amount oppgis i kroner. amount_ore
kan brukes dersom integrasjonen heller vil sende heltall i øre.
Produktlinjer fra gjentakende tabell
Når payment_line_items_mode er repeat,
sender integrasjonen radene til feltet som står i
payment_repeat_field_key. Kolonnenavnene er nøklene som
returneres i tabellfeltets columns.
Eksempel dersom tabellfeltet heter ordrelinjer og
betalingsfeltet mapper produkt, antall,
enhetspris og mva:
{
"template_id": 31,
"external_id": "erp:ordre:5833042:betaling",
"fields": {
"ordrenummer": "5833042",
"ordrelinjer": [
{
"produkt": "Montering",
"antall": 2,
"enhetspris": 1250,
"mva": 25
},
{
"produkt": "Kjøring",
"antall": 1,
"enhetspris": 500,
"mva": 25
}
]
},
"recipient": {
"name": "Ola Nordmann",
"phone": "+4791234567",
"email": "ola@example.no"
},
"receipt_buyer": {
"name": "Eksempelbedriften AS",
"organization_number": "999999999",
"address": "Eksempelveien 1",
"postal_code": "5003",
"city": "Bergen",
"country": "Norge",
"email": "regnskap@example.no"
}
}enhetspris oppgis i kroner og er inkludert MVA. Dokflow
beregner antall × enhetspris for hver rad og bruker summen
som betalingsbeløp. Ikke send et separat beløp for samme betalingsfelt.
Hvis ingen antallskolonne er mappet, brukes antall 1. Hvis
ingen MVA-kolonne er mappet, brukes betalingsfeltets
payment_vat_percent.
Produktlinjene fryses i forespørselen når den opprettes. De brukes
videre i Stripe Checkout, vises for kunden og legges på salgsbilaget.
Hver linje kan ha egen MVA-sats.
request.payments[].line_items returnerer produktlinjene for
et opprettet betalingsforsøk, og
request.payments[].receipt.credit_notes inneholder
eventuelle kreditnotaer og PDF-lenker.
Dokflow lager et maskinelt nummerert salgsbilag først når beløpet faktisk er captured; en ren depositumsreservasjon utsteder ikke kvittering. Ved Stripe-refund utstedes kreditnota automatisk.
Hent forespørselsstatus
Med Dokflow-ID:
GET /api/external/v1/requests.php?id=1234
Med ekstern ID:
GET /api/external/v1/requests.php?external_id=system%3Acase%3A84291%3Acontract
Responsen inneholder forespørselsstatus, workflow-status, mottaker, offentlig URL, submission-status, signaturdeltakere, signature_anchors og payments. Når et captured betalingsforsøk har fått salgsbilag, inneholder betalingsobjektet også receipt med bilagsnummer, beløp og PDF-URL.
Idempotens /
external_id
Det anbefales sterkt at alle opprettelser har en stabil
external_id fra kildesystemet, for eksempel:
system:case:84291:contract
Samme external_id + identisk JSON kan trygt sendes på
nytt. Dokflow returnerer da eksisterende objekt med:
{
"idempotent_replay": true
}Hvis samme external_id brukes med annet innhold, svarer
API-et med HTTP 409 og
code: "idempotency_conflict". Dette hindrer at retries
lager duplikater eller at en eksisterende ekstern referanse utilsiktet
får nytt innhold.
Modulbaserte endepunkter
Noen API-funksjoner følger Dokflow-moduler. V1 har foreløpig e-CMR som modulbasert API.
- Har organisasjonen e-CMR aktivert, kan
administrator gi
ecmr:readog/ellerecmr:write. - Har organisasjonen ikke e-CMR aktivert, vises ikke disse scopene i Innstillinger.
- Discovery (
GET /api/external/v1/) returnerer ikkeecmrunderendpoints. - Direkte kall til e-CMR-endepunktet returnerer
404 endpoint_not_available. - Hvis e-CMR senere deaktiveres, fjernes e-CMR-scopes fra organisasjonens eksisterende API-nøkler.
Dette gjør at integrasjoner kan bruke discovery-responsen som fasit i stedet for å anta at alle Dokflow-moduler finnes.
e-CMR
Hent e-CMR-systemskjema
GET /api/external/v1/ecmr.php?schema=1
Dette returnerer den interne
ECMR_SYSTEM_V1-strukturen.
Opprett e-CMR
POST /api/external/v1/ecmr.php
Eksempel:
{
"external_id": "transport:route:55021",
"data": {
"reference": "55021",
"issue_place": "Bergen",
"issue_date": "2026-08-28",
"pickup_place": "Eidsvågveien 150, 5105 Eidsvåg i Åsane, Norway",
"pickup_date": "2026-08-29T08:00",
"delivery_place": "Industrigatan 10, 211 20 Malmö, Sweden",
"delivery_date": "2026-08-30T14:00",
"vehicle_registration": "AB12345",
"trailer_registration": "CD6789",
"sender_instructions": "Lastes fra rampe 3.",
"customs_instructions": "",
"carriage_terms": "Freight prepaid",
"cash_on_delivery": "",
"special_instructions": "",
"sender": {
"company_name": "Avsender AS",
"name": "Anne Avsender",
"address": "Eidsvågveien 150",
"postal_code": "5105",
"city": "Eidsvåg i Åsane",
"country": "NO",
"org_number": "999999999",
"phone": "+4791111111",
"email": "anne@example.no"
},
"carrier": {
"company_name": "Transport AS",
"name": "Jan Sjåfør",
"address": "Transportveien 2",
"postal_code": "5000",
"city": "Bergen",
"country": "NO",
"org_number": "888888888",
"phone": "+4792222222",
"email": "jan@example.no"
},
"consignee": {
"company_name": "Mottaker AB",
"name": "Eva Mottaker",
"address": "Industrigatan 10",
"postal_code": "21120",
"city": "Malmö",
"country": "SE",
"org_number": "SE556000000001",
"phone": "+46701111111",
"email": "eva@example.se"
},
"goods": [
{
"marks": "PAL-001",
"packages": 4,
"packaging_type": "EUR-pall",
"description": "Kjøkkeninnredning",
"statistical_number": "94034010",
"gross_weight_kg": 720,
"volume_m3": 6.2,
"adr_un_number": "",
"adr_class": "",
"packing_group": ""
}
],
"reservations": []
},
"participants": {
"sender": {
"person_name": "Anne Avsender",
"phone": "+4791111111",
"email": "anne@example.no",
"require_otp": true,
"require_gps": false,
"require_id": false
},
"carrier": {
"person_name": "Jan Sjåfør",
"phone": "+4792222222",
"email": "jan@example.no",
"require_otp": true,
"require_gps": true,
"require_id": true
},
"consignee": {
"person_name": "Eva Mottaker",
"phone": "+46701111111",
"email": "eva@example.se",
"require_otp": true,
"require_gps": true,
"require_id": false
}
},
"mark_ready": true,
"send_sms": ["carrier", "consignee"]
}Hvis participants utelates, oppretter Dokflow automatisk
signeringspartene fra data.sender,
data.carrier og data.consignee med
e-CMR-standardkravene.
send_sms kan være:
false– ikke send SMStrue– send til alle tre roller- en liste, f.eks.
["carrier", "consignee"]
Når SMS skal sendes, blir e-CMR automatisk gjort klar først og alle obligatoriske CMR-data valideres.
Opprettelsen feiler ikke bare fordi en offentlig
OpenTimestamps-kalender er midlertidig utilgjengelig. I så fall
returneres en melding i ecmr.integrity_warnings, den frosne
PDF-en beholdes, og Dokflow forsøker automatisk å forankre den manglende
milepælen ved en senere vellykket forankring.
Hent e-CMR-status
GET /api/external/v1/ecmr.php?id=55
eller:
GET /api/external/v1/ecmr.php?external_id=transport%3Aroute%3A55021
Responsen inkluderer gjeldende revisjon, innholdshash, strukturert
e-CMR-data, signeringsparter, signaturer, offentlige vedlegg,
OpenTimestamps-forankringer (anchors), verifikasjons-URL og
resultatet av kontroll av audit-hashkjeden.
Dokflow forankrer viktige e-CMR-milepæler separat: opprettelse,
utstedelse/klar for signering, ny revisjon etter en endring, avsenders
signatur, transportør/sjåførs signatur og mottakers signatur. Hver
anchor inneholder derfor blant annet milestone,
revision_no, content_sha256,
signature_count, SHA-256 av den eksakte frosne PDF-en
(pdf_sha256), kalender og tidspunkt for innsending.
Når en e-CMR er utstedt (ready eller senere), er
revisjonen låst. Senere endringer i CMR-data, signeringsparter eller
offentlige vedlegg oppretter en ny revisjon i stedet for å overskrive den
tidligere versjonen.
Hent e-CMR som PDF
Gjeldende revisjon:
GET /api/external/v1/ecmr.php?id=55&format=pdf
En bestemt historisk revisjon:
GET /api/external/v1/ecmr.php?id=55&revision=2&format=pdf
PDF-kallet bruker samme Authorization: Bearer ...-header
og krever ecmr:read. Dersom den eksakte revisjonstilstanden
(samme content_sha256 og samme antall signaturer) er
OpenTimestamps-forankret, returnerer Dokflow den frosne PDF-en som
faktisk ble hashet og sendt til forankring. Et eldre anchor fra samme
revisjonsnummer brukes ikke dersom utkastet senere ble endret.
Hent OpenTimestamps-proof
For gjeldende revisjon:
GET /api/external/v1/ecmr.php?id=55&format=ots
For en historisk revisjon:
GET /api/external/v1/ecmr.php?id=55&revision=2&format=ots
Responsen er en standard .ots-fil for den nyeste
forankringen som dekker den eksakte revisjonstilstanden. Den kan lagres
sammen med PDF-en og senere oppgraderes/verifiseres uavhengig med en
OpenTimestamps-klient. Bare SHA-256-hashen av PDF-en sendes til den
eksterne kalenderen; selve e-CMR-dokumentet eller signaturbildene sendes
ikke.
Svarformat og feilsøking
Vanlige JSON-svar inneholder ok og
request_id.
Vellykket svar:
{
"ok": true,
"request_id": "7c5e3e8c0a5e..."
}Feilsvar:
{
"ok": false,
"error": "Forklaring på hva som er galt.",
"code": "validation_error",
"request_id": "7c5e3e8c0a5e..."
}Lagre gjerne request_id i loggen til kildesystemet. Da
er det mye enklere å finne det samme API-kallet i Dokflows
audit-logg.
HTTP-statuskoder
Vanlige svar:
200– lest eller idempotent replay201– opprettet401– ugyldig/utløpt/tilbakekalt API-nøkkel403– API-nøkkelen mangler nødvendig scope404– mal/objekt finnes ikke, eller et modulbasert endepunkt er ikke tilgjengelig for organisasjonen409–external_idallerede brukt med annet innhold422– ugyldige felt eller manglende data429– rate limit eller organisasjonens forespørselskvote er brukt opp503– nødvendig tjeneste/migrering er ikke klar
Rate limit og logging
En API-nøkkel kan gjøre opptil 300 kall per minutt. Alle API-kall logges med API-nøkkel, organisasjon, request-id, tidspunkt, HTTP-status, IP-adresse og user-agent.
Anbefalt integrasjonsflyt
En vanlig integrasjon kan gjøre følgende:
- Ved oppsett: kall
GET /api/external/v1/og les hvilke capabilities og endepunkter som er tilgjengelige. - Hent maler med
GET /templates.php, og la integrasjonen eller en administrator velge hvilken Dokflow-mal som skal brukes. - Lagre valgt
template_idog mapping mellom feltene i kildesystemet og Dokflowfield.key. - Når noe skal sendes til signering: kall
POST /requests.phpmed en stabilexternal_id. - Lagre returnert Dokflow
idog URL i kildesystemet. - Hent status ved behov med
GET /requests.php?external_id=..., for eksempel ved synkronisering eller når brukeren åpner saken. - Bruk modulbaserte endepunkter, som e-CMR, bare når de annonseres i discovery-responsen.
Eksempel på stabil external_id:
system:object:84291:agreement:v1
Bruk helst en stabil ID fra kildesystemet i stedet for en tilfeldig UUID. Det gjør retries idempotente og reduserer risikoen for duplikater.
Sikkerhet
API-et er laget for server-til-server bruk. CORS aktiveres ikke av V1. Lagre API-nøkkelen som en hemmelighet i kildesystemets backend/miljø, og gi hver integrasjon kun scopes den trenger. Tilbakekall nøkkelen i Dokflow hvis den kan ha kommet på avveie.