Koble Webspesialisten CRM til nettsiden, nettbutikken, regnskapet eller egne systemer. Et enkelt REST API med JSON, forutsigbare feilmeldinger på norsk og signerte webhooks.
API-et gir tilgang til de samme dataene som dere ser i CRM-et: bedrifter, kontakter, salgsmuligheter, aktiviteter, produkter, saker og tilbud. Alt som gjøres via API-et går gjennom de samme reglene som i appen – validering, tilgangskontroll, revisjonslogg og webhooks – og vises med en gang for kollegaene deres.
Alle forespørsler og svar er JSON. Send Content-Type: application/json på POST og PATCH.
ID-er er UUID. Tidspunkter er ISO 8601 i UTC (2026-09-26T10:15:00.000Z), datoer er YYYY-MM-DD.
Beløp er tall i NOK eks. mva. Vi godtar også norske tall som tekst, f.eks. "12 500,50".
Svar er pakket i { "data": … }. Lister har i tillegg next_cursor og has_more.
API-et krever planen Pro eller Bedrift (alt er åpent i prøveperioden).
Autentisering
Lag en API-nøkkel under Innstillinger → API og webhooks (krever administrator). Nøkkelen vises bare én gang – lagre den trygt, f.eks. som miljøvariabel. Send den i hver forespørsel:
Header
Authorization: Bearer wcrm_…
Nøkkelen opptrer som brukeren som lagde den (eller kontoeieren om brukeren er slettet), så «Opprettet av» og revisjonsloggen viser hvem som står bak integrasjonen. Velg minst mulig tilgang:
Tilgang (scope)
Kan brukes til
readLesetilgang
Kun GET-forespørsler.
writeLese- og skrivetilgang
Kan opprette, endre og slette data.
leadsKun skjema (leads)
Kan bare sende inn henvendelser til POST /api/v1/leads. Trygg å bruke i et nettskjema.
Hold nøkler hemmelige. Lese- og skrivenøkler skal aldri ligge i nettleserkode eller i git. Bare «leads»-nøkler er laget for å kunne ligge i et offentlig nettskjema. Tilbakekall en nøkkel med ett klikk hvis den kommer på avveie.
Hurtigstart
Sett nøkkelen i en miljøvariabel (export CRM_TOKEN=wcrm_…) og prøv:
# Test at nøkkelen virkercurl https://crm.webspesialisten.no/api/v1/me \
-H "Authorization: Bearer $CRM_TOKEN"# Opprett en kontaktcurl -X POST https://crm.webspesialisten.no/api/v1/contacts \
-H "Authorization: Bearer $CRM_TOKEN" \
-H "Content-Type: application/json" \
-d '{"first_name":"Kari","last_name":"Nordmann","email":"kari@firma.no","tags":["nettside"]}'
Svaret er hele kontakten, inkludert id og url til kontakten i CRM-et:
Alle lister er sidevis med cursor. Standard er 50 per side, maks 200. Når has_more er true, sender du next_cursor tilbake som cursor for å få neste side. Cursoren er stabil selv om det kommer nye rader underveis.
Parameter
Type
Beskrivelse
q
tekst
Fritekstsøk (delstreng, uavhengig av store/små bokstaver).
limit
heltall
Antall per side, 1–200. Standard 50.
cursor
tekst
Hent neste side: send verdien fra «next_cursor» i forrige svar.
order
tekst
Sortering på opprettet-tidspunkt. Standard «desc» (nyeste først).
descasc
updated_since
tidspunkt
Bare oppføringer endret etter dette tidspunktet (ISO 8601). Nyttig for synkronisering.
I tillegg har hver ressurs egne filtre (f.eks. company_id, status, tag) – se referansen under. Filtre kombineres med OG.
// Hent alle kontakter endret siden forrige synkasyncfunction* allContacts(since) {
let cursor = null;
do {
const qs = new URLSearchParams({ limit: "200", updated_since: since });
if (cursor) qs.set("cursor", cursor);
const res = awaitfetch(`https://crm.webspesialisten.no/api/v1/contacts?${qs}`, { headers });
const page = await res.json();
yield* page.data;
cursor = page.next_cursor;
} while (cursor);
}
forawait (const c of allContacts("2026-09-01T00:00:00Z")) console.log(c.email);
Feilhåndtering
Vi bruker vanlige HTTP-statuskoder. Feil har alltid samme form, med en maskinlesbar code og en norsk message som kan vises direkte til brukeren. Ved valideringsfeil sier fields hvilke felt som er feil.
Ugyldig eller manglende felt. «fields» sier hvilke.
400
invalid_json
Body er ikke gyldig JSON-objekt.
400
invalid_cursor
Cursor er ugyldig eller utløpt.
401
unauthorized
Mangler, ugyldig eller tilbakekalt API-nøkkel.
402
plan_limit
Planens grense er nådd (f.eks. antall kontakter).
403
insufficient_scope
Nøkkelen har ikke tilgang (lesenøkkel mot skriveendepunkt, eller leads-nøkkel).
403
plan_upgrade_required
API er ikke inkludert i planen.
403
forbidden
Brukeren bak nøkkelen har ikke tilgang.
403
account_suspended
Kontoen er suspendert eller avsluttet.
404
not_found
Finnes ikke (eller tilhører en annen konto).
405
method_not_allowed
Operasjonen støttes ikke for ressursen.
409
conflict
Konflikt, f.eks. oppføringen er i bruk.
413
payload_too_large
Body over 1 MB.
429
rate_limited
For mange forespørsler. Vent til «Retry-After» sekunder.
500
internal_error
Feil hos oss. Prøv igjen senere.
Rategrenser
Hver nøkkel kan gjøre 600 forespørsler per 10 minutter. Alle svar har headerne under. Går du over grensen får du 429 med Retry-After (sekunder). Trenger dere mer til en stor import? Ta kontakt, eller bruk importen i CRM-et.
X-RateLimit-Limit
Maks antall forespørsler i vinduet (600).
X-RateLimit-Remaining
Hvor mange du har igjen.
X-RateLimit-Reset
Når vinduet nullstilles (Unix-tid, sekunder).
Retry-After
Bare ved 429: sekunder til du kan prøve igjen.
CORS er åpent for alle domener (Access-Control-Allow-Origin: *). API-et bruker ikke informasjonskapsler.
Referanse
Endepunkter
Generert fra samme kilde som openapi.json, så dokumentasjonen alltid er i takt med API-et.
Ett kall fra kontaktskjemaet på nettsiden: finner eller oppretter bedrift (org.nr./navn) og kontakt (e-post), oppretter eventuelt en salgsmulighet og lagrer meldingen som notat. Kan brukes med en «leads»-nøkkel, som ikke har tilgang til noe annet.
Body (JSON)
Felt
Type
Beskrivelse
name
tekst
Fullt navn (deles i fornavn/etternavn). Alternativ til first_name/last_name.
first_name
tekst
Fornavn.
last_name
tekst
Etternavn.
email
e-post
E-post. Brukes til å finne eksisterende kontakt. E-post eller telefon er påkrevd.
phone
tekst
Telefon.
mobile
tekst
Mobil.
title
tekst
Stilling.
company_name
tekst
Firmanavn. Eksisterende bedrift matches på org.nr., deretter navn.
org_number
tekst
Org.nr. (9 siffer, valideres).
website
URL
Firmaets nettside (ved ny bedrift).
message
tekst
Meldingen fra skjemaet. Lagres som notat på tidslinjen.
subject
tekst
Tittel på notatet. Standard «Henvendelse via nettskjema».
source
tekst
Kilde. Standard «Nettskjema».
deal_title
tekst
Opprett en salgsmulighet med denne tittelen.
deal_value
tall
Verdi på salgsmuligheten (NOK eks. mva). Oppretter salgsmulighet selv uten tittel.
consent_marketing
true/false
Samtykke til markedsføring (avkrysset i skjemaet).
consent_source
tekst
Hvor samtykket ble gitt. Standard = source.
tags
liste med tekst
Tagger på ny kontakt/bedrift.
Svar
201 IDer til det som ble funnet/opprettet. Mulige feil: 400401402403429
Felt
Type
Beskrivelse
company_id
UUID · kan være null
Bedriften (ny eller eksisterende).
contact_id
UUID
Kontakten (ny eller eksisterende).
deal_id
UUID · kan være null
Ny salgsmulighet.
activity_id
UUID · kan være null
Notatet med meldingen.
created
objekt
Hva som ble opprettet: { company, contact, deal } (boolean).
Eksempel
curl -X POST "https://crm.webspesialisten.no/api/v1/leads" \
-H "Authorization: Bearer $CRM_TOKEN" \
-H "Content-Type: application/json" \
-d '{"name":"Kari Nordmann","email":"kari@firma.no","phone":"912 34 567","company_name":"Firma AS","message":"Vi ønsker tilbud på nytt nettsted.","deal_value":50000,"consent_marketing":true}'
Personregisteret. En kontakt kan være knyttet til en bedrift. Minst ett av fornavn, etternavn eller e-post må fylles ut. Sidevis med cursor, nyeste først.
Parametere
Parameter
Type
Beskrivelse
company_id
UUID
Kontakter i denne bedriften.
email
e-post
Eksakt e-post (uavhengig av store/små bokstaver).
category
tekst
Kategori.
kundeleadprospektleverandorpartnertidligere
status
tekst
Status.
activeinactive
consent_marketing
true/false
true = bare kontakter med markedsføringssamtykke.
owner_id
UUID
Bare oppføringer med denne ansvarlige.
tag
tekst
Bare oppføringer med denne taggen (navn, uavhengig av store/små bokstaver).
Salgsmuligheter i pipelinen. Flytt en mulighet ved å endre «stage_id»; fasens type (open/won/lost) styrer status, «closed_at» og webhookene deal.won / deal.lost.
Objektet Deal – alle felt
Felt
Type
Beskrivelse
idkun les
UUID
Unik ID for salgsmuligheten.
title
tekst
Tittel.
company_id
UUID · kan være null
ID til bedriften.
contact_id
UUID · kan være null
ID til kontaktpersonen. Bedrift fylles ut fra kontakten om den mangler.
stage_id
UUID
Fase i pipelinen (se GET /pipeline-stages). Standard: første åpne fase.
stage_namekun les
tekst
Navnet på fasen.
statuskun les
tekst
Utledet fra fasen.
openwonlost
value
tall
Verdi i NOK eks. mva. For «recurring» er dette per måned.
recurring
true/false
Månedlig gjentakende inntekt (MRR).
probability
heltall · kan være null
Sannsynlighet 0–100 %. null = fasens standard.
effective_probabilitykun les
heltall
Sannsynligheten som faktisk brukes (egen eller fasens).
expected_close
dato · kan være null
Forventet lukkedato (YYYY-MM-DD).
closed_atkun les
tidspunkt · kan være null
Når muligheten ble vunnet/tapt.
source
tekst · kan være null
Kilde.
lost_reason
tekst · kan være null
Tapsårsak (for tapte muligheter).
description
tekst · kan være null
Beskrivelse.
owner_id
UUID · kan være null
Ansvarlig bruker (se GET /users). Standard: eieren av API-nøkkelen.
tagskun les
liste med tekst
Tagger (navn).
created_bykun les
UUID · kan være null
Brukeren som opprettet oppføringen (for API-kall: eieren av nøkkelen).
Salgsmuligheter i pipelinen. Flytt en mulighet ved å endre «stage_id»; fasens type (open/won/lost) styrer status, «closed_at» og webhookene deal.won / deal.lost. Sidevis med cursor, nyeste først.
Parametere
Parameter
Type
Beskrivelse
company_id
UUID
Bedrift.
contact_id
UUID
Kontakt.
stage_id
UUID
Fase.
status
tekst
Åpne, vunne eller tapte.
openwonlost
owner_id
UUID
Bare oppføringer med denne ansvarlige.
tag
tekst
Bare oppføringer med denne taggen (navn, uavhengig av store/små bokstaver).
Oppgaver, samtaler, møter og notater. Vises på tidslinjen til bedrift, kontakt og salgsmulighet. Notater og aktiviteter opprettet med «completed: true» registreres som utført.
Objektet Activity – alle felt
Felt
Type
Beskrivelse
idkun les
UUID
Unik ID for aktiviteten.
type
tekst
Type. Standard «task».
callemailmeetingtasknotefollow_uplunchdemo
title
tekst
Tittel. Standard: typens navn (f.eks. «Telefonsamtale»).
description
tekst · kan være null
Beskrivelse / notat.
due_at
tidspunkt · kan være null
Frist eller tidspunkt. ISO 8601, eller «YYYY-MM-DDTHH:mm» tolket som norsk tid.
duration_min
heltall · kan være null
Varighet i minutter.
location
tekst · kan være null
Sted.
priority
tekst
Prioritet. Standard «normal».
lownormalhighurgent
outcome
tekst · kan være null
Resultat av samtalen/møtet.
completed_atkun les
tidspunkt · kan være null
Når aktiviteten ble utført.
company_id
UUID · kan være null
ID til bedriften.
contact_id
UUID · kan være null
ID til kontakten.
deal_id
UUID · kan være null
ID til salgsmuligheten.
ticket_id
UUID · kan være null
ID til saken.
owner_id
UUID · kan være null
Ansvarlig bruker (se GET /users). Standard: eieren av API-nøkkelen.
created_bykun les
UUID · kan være null
Brukeren som opprettet oppføringen (for API-kall: eieren av nøkkelen).
Oppgaver, samtaler, møter og notater. Vises på tidslinjen til bedrift, kontakt og salgsmulighet. Notater og aktiviteter opprettet med «completed: true» registreres som utført. Sidevis med cursor, nyeste først.
Tilbud med linjer. Summene beregnes av CRM-et. Enkeltoppslag inkluderer linjene («items») og «public_url» – lenken kunden bruker for å se og akseptere tilbudet.
Tilbud kan leses og opprettes via API-et. Endring, sending på e-post og aksept gjøres i CRM-et eller av kunden via «public_url».
Objektet Quote – alle felt
Felt
Type
Beskrivelse
idkun les
UUID
Unik ID for tilbudet.
numberkun les
heltall
Tilbudsnummer.
title
tekst
Tittel.
company_id
UUID · kan være null
ID til bedriften.
contact_id
UUID · kan være null
ID til kontakten.
deal_id
UUID · kan være null
ID til salgsmuligheten.
status
tekst
Status. Ved opprettelse kan du sende «draft» (standard) eller «sent». Sending på e-post gjøres i CRM-et.
draftsentviewedacceptedrejectedexpired
issue_date
dato
Tilbudsdato. Standard i dag.
valid_until
dato · kan være null
Gyldig til. Standard 30 dager etter tilbudsdato.
intro
tekst · kan være null
Innledning.
notes
tekst · kan være null
Merknader.
terms
tekst · kan være null
Vilkår. Standard: kontoens vilkår.
subtotalkun les
tall
Sum eks. mva før rabatt.
discount_totalkun les
tall
Total rabatt.
vat_totalkun les
tall
Mva.
totalkun les
tall
Totalt inkl. mva.
public_urlkun les
URL · kan være null
Kundens lenke til tilbudet.
sent_atkun les
tidspunkt · kan være null
Sendt.
accepted_atkun les
tidspunkt · kan være null
Akseptert.
items
liste med objekter
Linjer.
idUUID – Linje-ID.
product_idUUID – Produkt (valgfritt).
descriptiontekst – Linjetekst.
quantitytall – Antall. Standard 1.
unittekst – Enhet. Standard «stk».
unit_pricetall – Enhetspris eks. mva.
discount_pcttall – Rabatt i prosent.
vat_ratetall – Mva-sats. Standard 25.
line_totaltall – Linjesum eks. mva etter rabatt.
sortheltall – Rekkefølge.
owner_id
UUID · kan være null
Ansvarlig bruker (se GET /users). Standard: eieren av API-nøkkelen.
created_bykun les
UUID · kan være null
Brukeren som opprettet oppføringen (for API-kall: eieren av nøkkelen).
Tilbud med linjer. Summene beregnes av CRM-et. Enkeltoppslag inkluderer linjene («items») og «public_url» – lenken kunden bruker for å se og akseptere tilbudet. Sidevis med cursor, nyeste først.
Med POST /api/v1/leads blir en henvendelse fra nettsiden til en komplett lead med ett kall:
Bedriften finnes eller opprettes (matcher på org.nr., deretter navn).
Kontakten finnes eller opprettes (matcher på e-post) og knyttes til bedriften. Samtykke til markedsføring lagres med tidspunkt.
Hvis du sender deal_title eller deal_value, opprettes en salgsmulighet i første fase.
Meldingen lagres som notat på tidslinjen, og webhooks sendes som vanlig.
Lag en nøkkel med tilgangen «Kun skjema (leads)». Den kan bare sende inn henvendelser – ikke lese eller endre noe – og kan derfor brukes direkte fra nettsiden. Enda bedre er å sende skjemaet via egen server.
Felt
Type
Beskrivelse
name
tekst
Fullt navn (deles i fornavn/etternavn). Alternativ til first_name/last_name.
first_name
tekst
Fornavn.
last_name
tekst
Etternavn.
email
e-post
E-post. Brukes til å finne eksisterende kontakt. E-post eller telefon er påkrevd.
phone
tekst
Telefon.
mobile
tekst
Mobil.
title
tekst
Stilling.
company_name
tekst
Firmanavn. Eksisterende bedrift matches på org.nr., deretter navn.
org_number
tekst
Org.nr. (9 siffer, valideres).
website
URL
Firmaets nettside (ved ny bedrift).
message
tekst
Meldingen fra skjemaet. Lagres som notat på tidslinjen.
subject
tekst
Tittel på notatet. Standard «Henvendelse via nettskjema».
source
tekst
Kilde. Standard «Nettskjema».
deal_title
tekst
Opprett en salgsmulighet med denne tittelen.
deal_value
tall
Verdi på salgsmuligheten (NOK eks. mva). Oppretter salgsmulighet selv uten tittel.
consent_marketing
true/false
Samtykke til markedsføring (avkrysset i skjemaet).
consent_source
tekst
Hvor samtykket ble gitt. Standard = source.
tags
liste med tekst
Tagger på ny kontakt/bedrift.
<form id="kontakt">
<input name="name" placeholder="Navn" required>
<input name="email" type="email" placeholder="E-post" required>
<input name="company_name" placeholder="Firma">
<textarea name="message" placeholder="Hva kan vi hjelpe med?"></textarea>
<label><input type="checkbox" name="consent_marketing"> Ja takk til nyhetsbrev</label>
<button>Send</button>
</form>
<script>
// Bruk en nøkkel med tilgang «Kun skjema (leads)» – den kan ikke lese data.const LEADS_TOKEN = "wcrm_…";
document.getElementById("kontakt").addEventListener("submit", async (e) => {
e.preventDefault();
const f = Object.fromEntries(new FormData(e.target));
const res = awaitfetch("https://crm.webspesialisten.no/api/v1/leads", {
method: "POST",
headers: { Authorization: "Bearer " + LEADS_TOKEN, "Content-Type": "application/json" },
body: JSON.stringify({ ...f, consent_marketing: !!f.consent_marketing, source: "Nettside" }),
});
e.target.innerHTML = res.ok ? "<p>Takk! Vi tar kontakt snart.</p>" : "<p>Noe gikk galt. Prøv igjen.</p>";
});
</script>
Webhooks
Webhooks gir systemet deres beskjed med en gang noe skjer i CRM-et – uten å spørre API-et hele tiden. Legg til en URL under Innstillinger → API og webhooks, velg hendelser, og ta vare på hemmeligheten (vises én gang).
Slik ser en levering ut
Header
Verdi
Content-Type
application/json
User-Agent
Webspesialisten-CRM-Webhook/1.0
X-Webhook-Event
Hendelsen, f.eks. contact.created
X-Webhook-Signature
sha256=<HMAC-SHA256 av rå body med hemmeligheten, hex>
Vi sender en POST med JSON til URL-en deres. Svar med 2xx innen 8 sekunder.
«data» er raden slik den ser ut etter endringen. Ved *.deleted inneholder «data» bare «id».
Leveringer prøves ikke på nytt automatisk. Siste status og feilmelding vises under Innstillinger → API og webhooks.
Bekreft alltid signaturen, og sammenlign i konstant tid.
Verifiser signaturen
Hver levering er signert med HMAC-SHA256 av den rå request-bodyen, med webhookens hemmelighet som nøkkel: X-Webhook-Signature: sha256=<hex>. Avvis forespørsler der signaturen ikke stemmer, og sammenlign i konstant tid.
import { createHmac, timingSafeEqual } from"node:crypto";
// Next.js route handler / Express med rå body. Bruk ALLTID rå body – ikke JSON.stringify(req.body).exportasyncfunction POST(req) {
const raw = await req.text();
const expected = "sha256=" + createHmac("sha256", process.env.CRM_WEBHOOK_SECRET).update(raw).digest("hex");
const got = req.headers.get("x-webhook-signature") ?? "";
const ok = got.length === expected.length && timingSafeEqual(Buffer.from(got), Buffer.from(expected));
if (!ok) returnnew Response("Ugyldig signatur", { status: 401 });
const { event, data } = JSON.parse(raw);
if (event === "contact.created") {
// … synk til nyhetsbrev, regnskap osv.
}
returnnew Response("ok");
}
Knappen «Send test» i innstillingene sender hendelsen ping, signert på samme måte, så du kan teste mottakeren.
Hendelser
Hendelse
Sendes når
company.created
Bedrift opprettet
company.updated
Bedrift endret
company.deleted
Bedrift slettet
contact.created
Kontakt opprettet
contact.updated
Kontakt endret
contact.deleted
Kontakt slettet
deal.created
Salgsmulighet opprettet
deal.updated
Salgsmulighet endret
deal.won
Salgsmulighet vunnet
deal.lost
Salgsmulighet tapt
deal.deleted
Salgsmulighet slettet
activity.created
Aktivitet opprettet
activity.completed
Aktivitet fullført
quote.created
Tilbud opprettet
quote.sent
Tilbud sendt
quote.accepted
Tilbud akseptert av kunden
quote.rejected
Tilbud avslått av kunden
ticket.created
Sak opprettet
ticket.updated
Sak endret
sale.created
Salg registrert
contract.created
Kontrakt opprettet
Velg «Alle hendelser» (*) for også å få nye hendelser vi legger til senere.
OpenAPI 3.1 – Importer openapi.json i Postman, Insomnia eller en kodegenerator for å få ferdige klienter. Spørsmål? Kontakt oss – vi hjelper gjerne med integrasjonen.