Julekalender-API

Opret digitale julekalendere fra din egen software, udfyld lågerne og se, hvor ofte hver låge blev åbnet. Alt det, du kan i editoren på julekalenderskaber.dk, kan du gøre med nogle få HTTP-kald.

Sidst opdateret: 2026-09-29

Få en API-nøgle

Adgang til API og MCP er inkluderet i Premium (1.499 kr om året) og Enterprise (9.999 kr om året). Skriv til [email protected] med din kontos e-mailadresse, så sender vi dig en testnøgle og en live-nøgle.

Anmod om en nøgle

Overblik

Julekalender-API'et er et JSON REST API. Du opretter en kalender med et datointerval, et åbningstidspunkt og et design og lægger derefter indhold bag hver låge. Kalenderen er med det samme live på sit offentlige link, og hver låge åbner af sig selv på det indstillede tidspunkt i kalenderens tidszone.

Typiske anvendelser:

  • Webshops og bureauer, der hver december kører en kuponkalender for mange brands og ikke vil udfylde 24 låger i hånden hver gang.
  • Nyhedsbrevs- og CRM-værktøjer, der tilbyder en julekalender som kampagnetype.
  • HR- og intern kommunikation, der udfylder en teamkalender ud fra et regneark.
  • Udviklere, der vil lade en AI-agent bygge kalenderen. Til det findes MCP-serveren.

Base-URL: https://api.julekalenderskaber.dk/v1. Alle forespørgsler bruger HTTPS, alle body-data er UTF-8 JSON, og alle tidsstempler er ISO 8601.

Adgang og priser

API-adgang er en del af de betalte planer og koster ikke ekstra. Der er ingen gebyrer pr. kald.

  • Premium (1.499 kr om året): fuldt API med alle indholdstyper, egne datoer, branding og statistik.
  • Enterprise (9.999 kr om året): desuden adgangskodebeskyttelse, sporingskoder og CSV-eksport.
  • Free: ingen API-nøgle. Personlige kalendere forbliver gratis i editoren på julekalenderskaber.dk.

Nøgler udstedes i hånden. Skriv til [email protected] fra din kontos e-mailadresse, og fortæl kort, hvad du bygger. Du får som regel en testnøgle og en live-nøgle samme hverdag.

De betalte planer er tænkt til virksomheder. Hvis du bygger et produkt oven på API'et, der betjener mange slutkunder, så skriv det i din henvendelse, så vi kan dimensionere grænserne til dig.

Autentificering

Send din nøgle som bearer-token i headeren Authorization. Nøgler, der starter med sk_test_, virker i testtilstand, mens nøgler, der starter med sk_live_, opretter rigtige, offentlige kalendere.

bash Authorization-header i hver forespørgsel
curl https://api.julekalenderskaber.dk/v1/calendars \
  -H "Authorization: Bearer $ACM_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "title": "Northwind Coffee Advent Calendar",
    "start_date": "2026-12-01",
    "end_date": "2026-12-24",
    "opening_time": "08:00",
    "timezone": "Europe/Berlin",
    "template_style": "cozy"
  }'

Opbevar nøgler på serveren. En nøgle har rettighederne for hele kontoen, så læg den aldrig i browserkode eller i en mobilapp. Hvis en nøgle lækker, så skriv til os, så spærrer vi den inden for få minutter og sender dig en ny.

Kom hurtigt i gang

Den korteste vej til en fungerende kalender: opret den, udfyld lågerne i ét kald, og del linket.

python Python: 24 låger i to forespørgsler
import os, requests

API = "https://api.julekalenderskaber.dk/v1"
HEAD = {"Authorization": "Bearer " + os.environ["ACM_API_KEY"]}

cal = requests.post(f"{API}/calendars", headers=HEAD, json={
    "title": "Team Advent Calendar 2026",
    "start_date": "2026-12-01",
    "end_date": "2026-12-24",
    "opening_time": "09:00",
    "timezone": "Europe/Copenhagen",
}).json()

doors = [{"day": d, "content_type": "text", "title": f"Day {d}",
          "text": f"Surprise number {d} is waiting in the kitchen."}
         for d in range(1, cal["door_count"] + 1)]

requests.put(f"{API}/calendars/{cal['id']}/doors", headers=HEAD,
             json={"doors": doors}).raise_for_status()

print(cal["public_url"])   # share this link

Svaret på det første kald indeholder allerede door_count (udledt af datointervallet) og public_url. Låger uden indhold viser en venlig pladsholder, indtil du udfylder dem.

Endpoints

MetodeStiFormål
POST/v1/calendarsOpret en kalender.
GET/v1/calendars/{id}Læs én kalender.
GET/v1/calendarsList dine kalendere, nyeste først.
PATCH/v1/calendars/{id}Ret en kalenders indstillinger. Kun de felter, du sender, ændres.
DELETE/v1/calendars/{id}Slet en kalender med alle låger, medier og statistik.
GET/v1/calendars/{id}/doorsAlle låger i en kalender med åbningstidspunkter og indhold.
PUT/v1/calendars/{id}/doorsSkriv op til 50 låger i ét kald.
GET/v1/calendars/{id}/doors/{day}Læs en enkelt låge.
PUT/v1/calendars/{id}/doors/{day}Skriv eller erstat indholdet af en enkelt låge.
POST/v1/mediaUpload et billede, en video, en lydfil eller en download til en låge.
GET/v1/backgroundsBaggrundsdesigns, skabelonstile og tags.
GET/v1/calendars/{id}/statsVisninger, lågeåbninger, klik og deltagelser.
GET/v1/calendars/{id}/stats/exportDaglig statistik som CSV (Enterprise).
GET/v1/accountPlan, kvoter og aktiverede funktioner for nøglen.

Opret en kalender

POST /v1/calendars opretter en kalender og returnerer den. Kun title er påkrævet. Alt andet har fornuftige standardværdier (1. til 24. december i den kommende sæson (efter 24. december altså næste år), lågerne åbner kl. 00:00, tidszone Europe/Berlin, stil classic).

javascript Node.js: kalender med branding og baggrund
import { randomUUID } from "node:crypto";

const res = await fetch("https://api.julekalenderskaber.dk/v1/calendars", {
  method: "POST",
  headers: {
    "Authorization": `Bearer ${process.env.ACM_API_KEY}`,
    "Content-Type": "application/json",
    "Idempotency-Key": randomUUID(),
  },
  body: JSON.stringify({
    title: "Northwind Coffee Advent Calendar",
    description: "A new offer behind every door until Christmas Eve.",
    start_date: "2026-12-01",
    end_date: "2026-12-24",
    opening_time: "08:00",
    timezone: "Europe/Berlin",
    template_style: "corporate",
    background_image: "golden-baubles",
    custom_color: "#0F766E",
    door_colors: { door: "#0F766E", accent: "#F5C451", number: "#FFFFFF" },
    custom_logo_url: "https://cdn.northwind.example/logo.png",
    animation: "stars",
    metadata: { campaign: "xmas-2026" },
  }),
});
const calendar = await res.json();
FeltTypeBeskrivelse
titlestring, requiredVises over kalenderen. Op til 255 tegn.
descriptionstringKort tekst under titlen. Op til 2000 tegn.
start_datedateDagen for låge 1. Standard er 1. december. Premium.
end_datedateDagen for den sidste låge. Intervallet må højst dække 50 dage. Premium.
opening_timeHH:MMLokal tid, hvor hver låge åbner, for eksempel 08:00. Premium.
timezonestringIANA-tidszonenavn, for eksempel Europe/Copenhagen eller America/New_York. Standard er Europe/Berlin.
template_stylestringEn af de over 100 lågestile fra /v1/backgrounds.
background_imagestringNøgle til et baggrundsdesign fra kataloget.
custom_color#RRGGBBBrandfarve som hex-værdi. Premium.
door_colorsobjectEgne lågefarver oven på den valgte stil: door, accent og number som hex-værdier. null beholder stilens farver. Premium.
custom_logo_urlstringHTTPS-URL til dit logo, som vises over lågerne. Premium.
animationstringBaggrundsanimation: none, snow, flurry (tæt snefygning) eller stars (funklende stjerner). Standard er snow. Gratis i alle abonnementer.
snow_enabledbooleanÆldre genvej, true ved snow eller flurry. false sætter animation til none.
is_publicbooleanfalse skjuler kalenderen for alle andre end dig; den, der åbner linket, ser kun en kort besked om, at den ikke er delt endnu. Standard er true.
passwordstringBesøgende skal indtaste denne adgangskode, før de ser kalenderen. Gemmes kun som hash. Enterprise.
tracking_codestringDit eget analyse- eller pixel-snippet, som indlæses på den offentlige side. Enterprise.
metadataobjectOp til 20 egne nøgle/værdi-par, som returneres uændret og følger med i hver webhook.

Hvis du sender et felt, som din plan ikke omfatter, fejler hele forespørgslen med 403 plan_error, og der oprettes intet. Se Planer og funktioner.

Kalenderobjektet

json Svar fra POST /v1/calendars
{
  "id": "cal_4Hq8nW2kLp",
  "object": "calendar",
  "slug": "k7x2mq9t",
  "public_url": "https://julekalenderskaber.dk/c/k7x2mq9t/",
  "title": "Northwind Coffee Advent Calendar",
  "description": "A new offer behind every door until Christmas Eve.",
  "start_date": "2026-12-01",
  "end_date": "2026-12-24",
  "door_count": 24,
  "opening_time": "08:00",
  "timezone": "Europe/Berlin",
  "template_style": "corporate",
  "background_image": "golden-baubles",
  "custom_color": "#0F766E",
  "door_colors": { "door": "#0F766E", "accent": "#F5C451", "number": "#FFFFFF" },
  "custom_logo_url": "https://cdn.northwind.example/logo.png",
  "animation": "stars",
  "snow_enabled": false,
  "is_public": true,
  "password_protected": false,
  "tracking_code": null,
  "doors_filled": 0,
  "created_at": "2026-09-29T09:14:03Z",
  "metadata": { "campaign": "xmas-2026" },
  "livemode": true
}
FeltTypeBeskrivelse
idstringStabilt id, brug det i alle videre kald.
slugstringDel af det offentlige link, tilfældig ved oprettelse. Kun læsbar via API’et; du kan ændre den i editoren (små bogstaver, tal og bindestreger, 3 til 60 tegn).
public_urlstringLinket, du deler. Åbner kalenderen på julekalenderskaber.dk.
door_countintegerAntal låger, altid lig med antallet af dage fra start_date til end_date.
doors_filledintegerHvor mange låger der allerede har indhold.
password_protectedbooleantrue, hvis der er sat en adgangskode. Selve adgangskoden returneres aldrig.
livemodebooleanfalse for kalendere oprettet med en testnøgle.

List, ret og slet

GET /v1/calendars returnerer dine kalendere i sider på op til 100. Med active_on får du kun kalendere, hvis datointerval indeholder den dag. Med metadata[campaign]=xmas-2026 filtrerer du på dine egne metadata.

bash Kalendere, der kører den 10. december
curl -G https://api.julekalenderskaber.dk/v1/calendars \
  -H "Authorization: Bearer $ACM_API_KEY" \
  -d active_on=2026-12-10 \
  -d limit=50
json Svar med cursor
{
  "object": "list",
  "data": [
    { "id": "cal_4Hq8nW2kLp", "title": "Northwind Coffee Advent Calendar", "...": "..." },
    { "id": "cal_9Tz3pR6wQs", "title": "Team Advent Calendar 2026", "...": "..." }
  ],
  "has_more": false,
  "next_cursor": null
}

Hvis has_more er true, så send next_cursor som cursor for at hente næste side.

PATCH /v1/calendars/{id} ændrer kun de felter, du sender. Hvis du forkorter datointervallet, beholdes låger efter den nye slutning, men de skjules. Forlænger du intervallet igen, kommer de tilbage med deres indhold.

bash Åbn lågerne en time tidligere og slå snefald til
curl -X PATCH https://api.julekalenderskaber.dk/v1/calendars/cal_4Hq8nW2kLp \
  -H "Authorization: Bearer $ACM_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "opening_time": "07:00", "animation": "snow" }'

DELETE /v1/calendars/{id} fjerner kalenderen, alle låger, uploadede medier og statistik for altid. Det offentlige link giver derefter 404.

Låger

En kalender har præcis én låge pr. dag i sit datointerval, nummereret fra 1. Låger oprettes eller slettes ikke af sig selv. Du skriver kun deres indhold.

PUT /v1/calendars/{id}/doors/{day} sætter indholdet af én låge og erstatter det, der var der før.

bash En rabatkode bag låge 7
curl -X PUT https://api.julekalenderskaber.dk/v1/calendars/cal_4Hq8nW2kLp/doors/7 \
  -H "Authorization: Bearer $ACM_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "content_type": "coupon",
    "title": "Door 7: 20 % off all filter coffee",
    "text": "Valid today only, online and in store.",
    "coupon_code": "ADVENT07"
  }'
json Lågeobjektet
{
  "object": "door",
  "calendar_id": "cal_4Hq8nW2kLp",
  "day": 7,
  "date": "2026-12-07",
  "unlocks_at": "2026-12-07T08:00:00+01:00",
  "unlocked": false,
  "unlocked_override": false,
  "content_type": "coupon",
  "title": "Door 7: 20 % off all filter coffee",
  "text": "Valid today only, online and in store.",
  "url": null,
  "coupon_code": "ADVENT07",
  "data": {},
  "media": null,
  "opens": 0
}

unlocks_at angives altid med offset for kalenderens tidszone. opens tæller, hvor ofte besøgende har åbnet denne låge. Feltet udfyldes kun på Premium og Enterprise.

Indholdstyper

Hver låge har én content_type. Sådan bruger de felterne:

TypePlanFelter og visning
textFreetitle og text. Fra Premium kan text indeholde simpel HTML (afsnit, overskrifter, fed, kursiv, lister, links, billeder). Scripts, styles og event-attributter fjernes. På Free er det ren tekst.
embedFreeurl til en YouTube- eller Vimeo-video, vist som indlejret afspiller.
linkPremiumurl samt valgfri title og text, vist som en knap til din side. data.button bestemmer knappens tekst.
imagePremiummedia_id for et uploadet billede, valgfri title og text som billedtekst.
videoPremiummedia_id for en uploadet MP4-video.
couponPremiumcoupon_code samt title og text. Koden vises med en kopiknap. Vi genererer eller validerer aldrig koder. De kommer fra dit shopsystem.
quizPremiumtext indeholder spørgsmålet eller gåden, data.answer svaret. Med data.options (op til 6 svarmuligheder) vælger besøgende et svar og ser straks, om det er rigtigt. Uden svarmuligheder viser de svaret med et tryk.
productPremiumtitle som produktnavn, text som beskrivelse, valgfri media_id for et produktbillede, data.price som visningstekst og url til produktsiden. data.button bestemmer knappens tekst.
eventPremiumdata.date (ÅÅÅÅ-MM-DD, påkrævet), valgfri data.time (TT:MM) og data.location, samt text og en valgfri tilmeldings-url. Besøgende kan gemme eventet i deres egen kalender som .ics-fil.
giveawayPremiumdata.prize, valgfri data.deadline (ÅÅÅÅ-MM-DD), text med betingelserne og url til din deltagerformular. Vi indsamler ikke deltagelser og trækker ikke lod om vindere. Det sker via din formular.
downloadPremiummedia_id for en uploadet fil samt text. Vises med filnavn og en downloadknap. data.button bestemmer knappens tekst.
surveyPremiumtext indeholder spørgsmålet, data.options 2 til 6 svar. Hver browser stemmer én gang og ser derefter resultatet. Hvis du ændrer svarene, nulstilles stemmerne.
musicPremiummedia_id for en uploadet lydfil eller url til en Spotify- eller SoundCloud-side, valgfri data.artist.

Hvornår lågerne åbner

Låge n åbner på start_date + (n - 1) days på tidspunktet opening_time i kalenderens timezone. Den besøgendes tidszone er ligegyldig: en kalender indstillet til 08:00 Europe/Berlin åbner låge 1 kl. 07:00 i London og kl. 02:00 i New York.

Inden det tidspunkt viser den offentlige side lågen som lukket, og API'et bag den offentlige side afslører ikke dens indhold. Som ejer ser du altid alle låger.

Vil du åbne en låge tidligt (til en forhåndsvisning, en pressemeddelelse eller en bonusdag), så sæt "unlocked_override": true på lågen. Sætter du det tilbage til false, lukkes lågen igen indtil sit almindelige tidspunkt.

Ændringer af opening_time eller timezone gælder med det samme for alle låger, der endnu ikke er åbnet. Låger, der allerede er åbne, forbliver åbne.

Udfyld mange låger på én gang

PUT /v1/calendars/{id}/doors tager en liste med op til 50 låger. Låger, du ikke nævner, forbliver uændrede. Kaldet er alt eller intet: hvis én låge er ugyldig, skrives ingen låger, og fejlen nævner dag og felt.

json Request body med fire indholdstyper
{
  "doors": [
    { "day": 1, "content_type": "text", "title": "Welcome", "text": "<p>Glad you are here.</p>" },
    { "day": 2, "content_type": "embed", "url": "https://www.youtube.com/watch?v=abc123" },
    { "day": 3, "content_type": "link", "title": "Our winter blend", "url": "https://northwind.example/winter" },
    { "day": 4, "content_type": "image", "media_id": "med_2Kd7sF1vXp" }
  ]
}

Det er det kald, du skal bruge, når du udfylder en kalender ud fra et regneark eller et produktfeed. Det tæller som én forespørgsel i forhold til rate limit.

Billeder og videoer

Upload filer med POST /v1/media, og henvis derefter til det returnerede id som media_id i en låge. Du kan sende filen som multipart form data eller give os en offentlig source_url, som vi henter.

bash Upload en fil eller hent fra en URL
curl https://api.julekalenderskaber.dk/v1/media \
  -H "Authorization: Bearer $ACM_API_KEY" \
  -F "[email protected]" \
  -F "calendar_id=cal_4Hq8nW2kLp"

# or let us fetch it from a public URL
curl https://api.julekalenderskaber.dk/v1/media \
  -H "Authorization: Bearer $ACM_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "calendar_id": "cal_4Hq8nW2kLp", "source_url": "https://cdn.northwind.example/door-04.webp" }'
json Medieobjektet
{
  "id": "med_2Kd7sF1vXp",
  "object": "media",
  "calendar_id": "cal_4Hq8nW2kLp",
  "kind": "image",
  "mime_type": "image/webp",
  "bytes": 184322,
  "url": "https://julekalenderskaber.dk/media/4711/door-04.webp/",
  "created_at": "2026-09-29T09:20:41Z"
}
  • Billeder: JPG, PNG, GIF eller WebP, op til 2 MB.
  • Videoer: MP4, op til 20 MB.
  • Lyd: MP3, M4A eller OGG, op til 10 MB.
  • Downloads: PDF, ZIP, EPUB, DOCX, XLSX, PPTX eller TXT, op til 10 MB.
  • Medier hører til én kalender og slettes sammen med den.
  • Medieupload kræver Premium eller Enterprise.

Designs og baggrunde

GET /v1/backgrounds returnerer baggrundskataloget, de over 100 lågestile og de tags, du kan filtrere på. Brug en baggrunds key som background_image, når du opretter en kalender.

bash Kun baggrunde med tagget corporate
curl -G https://api.julekalenderskaber.dk/v1/backgrounds \
  -H "Authorization: Bearer $ACM_API_KEY" \
  -d tag=corporate
json Svar
{
  "object": "list",
  "data": [
    { "key": "golden-baubles", "name": "Golden Baubles", "tags": ["festive", "elegant", "corporate"],
      "thumb_url": "https://julekalenderskaber.dk/backgrounds/thumbs/golden-baubles.webp/" },
    { "key": "nordic-office", "name": "Nordic Office", "tags": ["minimalist", "corporate"],
      "thumb_url": "https://julekalenderskaber.dk/backgrounds/thumbs/nordic-office.webp/" }
  ],
  "template_styles": ["classic", "winter", "modern", "cozy", "corporate", "snowfall",
                      "candycane", "forest", "starlight", "gingerbread", "nordic", "midnight",
                      "plaid-red", "knit-red", "window-red", "bauble-red", "polka-red", "gift-kraft",
                      "frost-mint", "flat-retro", "starlight-teal", "knit-cream", "window-blue", "bauble-gold",
                      "gift-cranberry", "gift-frosted", "gift-pastel", "gift-emerald", "frost-lavender", "frost-blush",
                      "frost-glacier", "frost-silver", "flat-sorbet", "flat-ocean", "flat-mono", "flat-xmas",
                      "cabin-birch", "cabin-red", "cabin-green", "cabin-blue", "card-teal", "card-navy",
                      "card-dark", "card-cream", "card-green", "snowfall-teal", "snowfall-plum", "snowfall-red",
                      "snowfall-green", "snowfall-grey", "stripes-mint", "stripes-gold", "stripes-blue", "stripes-berry",
                      "stripes-cocoa", "forest-dusk", "forest-snow", "forest-red", "forest-midnight", "forest-pine",
                      "starlight-silver", "starlight-rose", "starlight-red", "starlight-green", "iced-pink", "iced-choc",
                      "iced-mint", "iced-honey", "linen-blue", "linen-green", "linen-red", "linen-dark",
                      "lacquer-silver", "lacquer-red", "lacquer-green", "lacquer-blue", "lacquer-rose", "plaid-green",
                      "plaid-blue", "plaid-buffalo", "plaid-grey", "plaid-camel", "plaid-mix", "knit-green",
                      "knit-navy", "knit-grey", "knit-mix", "window-green", "window-wood", "window-white",
                      "window-mix", "bauble-silver", "bauble-blue", "bauble-mix", "bauble-pink", "polka-green",
                      "polka-navy", "polka-pink", "polka-cream", "polka-mix", "knit-teal", "bauble-plum",
                      "stripes-frost", "card-red"],
  "tags": ["festive", "nature", "cozy", "elegant", "minimalist", "cartoon",
           "kids", "dogs", "cats", "corporate", "romantic"]
}

Statistik

GET /v1/calendars/{id}/stats returnerer visningerne af kalendersiden, visningerne de seneste syv dage, visninger og åbninger pr. dag samt for hver låge åbningerne og handlingerne i den (actions: klik på knapper, kopierede rabatkoder, quizsvar, deltagelser i konkurrencer og stemmer i afstemninger, talt én gang pr. besøgende). Statistik er en del af Premium og Enterprise.

json Svar
{
  "object": "calendar_stats",
  "calendar_id": "cal_4Hq8nW2kLp",
  "views": 18342,
  "views_last_7_days": 6120,
  "door_opens": 41877,
  "daily": [
    { "date": "2026-12-01", "views": 2410, "opens": 3904 },
    { "date": "2026-12-02", "views": 2265, "opens": 3517 }
  ],
  "doors": [
    { "day": 1, "type": "coupon", "opens": 3904, "actions": 1288 },
    { "day": 2, "type": "quiz", "opens": 3517, "actions": 2140 },
    { "day": 3, "type": "text", "opens": 3288, "actions": 0 }
  ],
  "generated_at": "2026-12-03T10:00:00Z"
}

På Enterprise returnerer /stats/export de samme data pr. dag som CSV, klar til et regneark eller dit BI-værktøj.

bash CSV-eksport for december
curl -L -o opens.csv \
  "https://api.julekalenderskaber.dk/v1/calendars/cal_4Hq8nW2kLp/stats/export?from=2026-12-01&to=2026-12-24" \
  -H "Authorization: Bearer $ACM_API_KEY"

# date,day,views,unique_visitors,door_opens,actions
# 2026-12-01,1,2410,1873,3904,612

Vi tæller visninger og åbninger uden cookies og uden at gemme IP-adresser i klartekst. Se Privatliv.

Planer og funktioner

Din nøgle har funktionerne fra din plan. GET /v1/account viser dem.

json GET /v1/account
{
  "object": "account",
  "plan": "enterprise",
  "renews_at": "2027-09-12",
  "calendars": { "used": 14, "limit": null },
  "features": {
    "media": true, "html": true, "custom_dates": true, "branding": true,
    "analytics": true, "password": true, "tracking_code": true, "csv_export": true
  },
  "livemode": true
}
FunktionFra planBerører
Premiumlink, image, video, coupon, quiz, product, event, giveaway, download, survey, musicLåger med links, billeder, videoer, rabatkoder, quizzer, produkter, events, konkurrencer, downloads, afstemninger og musik.
PremiumtextHTML-formatering i lågetekst.
Premiumstart_date, end_date, opening_timeEget datointerval og åbningstidspunkt i stedet for 1. til 24. december ved midnat.
Premiumcustom_color, door_colors, custom_logo_urlEgen brandfarve, egne lågefarver og eget logo.
Premium/statsVisninger og åbninger af låger.
EnterprisepasswordAdgangskodebeskyttede kalendere.
Enterprisetracking_codeEgen sporings- eller pixelkode på den offentlige side.
Enterprise/stats/exportStatistik som CSV.

Antallet af kalendere er ikke begrænset på de betalte planer.

Webhooks

Fortæl os et HTTPS-endpoint, så sender vi en hændelse dertil, hver gang der sker noget på din konto. Hver hændelse er et POST med en JSON-body.

HændelseHvornår
calendar.createdEn kalender blev oprettet, via API eller i editoren.
calendar.updatedIndstillinger for en kalender eller indholdet af en låge blev ændret.
calendar.deletedEn kalender blev slettet.
door.unlockedEn låge er lige åbnet. Praktisk, hvis du vil sende en pushbesked eller et nyhedsbrev i samme minut.
stats.dailyÉn gang om dagen, kort efter midnat i kalenderens tidszone: tallene fra dagen før. Fra Premium.
json Hændelsen door.unlocked
{
  "id": "evt_7Nc2vB5xRt",
  "type": "door.unlocked",
  "created_at": "2026-12-07T07:00:02Z",
  "livemode": true,
  "data": {
    "calendar_id": "cal_4Hq8nW2kLp",
    "day": 7,
    "unlocks_at": "2026-12-07T08:00:00+01:00",
    "public_url": "https://julekalenderskaber.dk/c/k7x2mq9t/",
    "metadata": { "campaign": "xmas-2026" }
  }
}

Hver forespørgsel har en ACM-Signature-header med et tidsstempel og en HMAC SHA256 af timestamp.body, signeret med din webhook-hemmelighed.

http Signatur-header
ACM-Signature: t=1796626802,v1=9e4b1c7d2a...
python Python: verificér signaturen
import hmac, hashlib, os, time

SECRET = os.environ["ACM_WEBHOOK_SECRET"].encode()

def verify(header: str, raw_body: bytes) -> bool:
    parts = dict(p.split("=", 1) for p in header.split(","))
    ts, sig = parts["t"], parts["v1"]
    if abs(time.time() - int(ts)) > 300:
        return False
    expected = hmac.new(SECRET, ts.encode() + b"." + raw_body, hashlib.sha256).hexdigest()
    return hmac.compare_digest(expected, sig)

Svar med en vilkårlig 2xx-status inden for 10 sekunder. Får vi ikke et 2xx, prøver vi igen med voksende mellemrum i 24 timer. Hændelser kan komme mere end én gang. Brug id til at ignorere dubletter.

Idempotens

Send en Idempotency-Key-header med hvert POST. Hvis forbindelsen afbrydes, og du sender den samme forespørgsel igen med samme nøgle inden for 24 timer, får du det oprindelige svar tilbage i stedet for en ekstra kalender.

bash Opret en kalender sikkert
curl https://api.julekalenderskaber.dk/v1/calendars \
  -H "Authorization: Bearer $ACM_API_KEY" \
  -H "Idempotency-Key: 3f6c1a2e-8d4b-4e0f-9b7a-5c2d1e0f9a88" \
  -H "Content-Type: application/json" \
  -d @calendar.json

PUT og PATCH er idempotente af natur og behøver ikke headeren.

Fejl

Fejl kommer med en passende HTTP-status og en body, der fortæller, hvad du skal rette.

json 403, når et felt kræver en højere plan
{
  "error": {
    "type": "plan_error",
    "code": "feature_not_in_plan",
    "message": "Password protection is part of the Enterprise plan.",
    "field": "password",
    "required_plan": "enterprise",
    "request_id": "req_5Xk1dM8pWq"
  }
}
TypeStatusBetydning
invalid_request400Forespørgslen kan ikke læses, for eksempel på grund af ødelagt JSON eller et ukendt felt.
authentication_error401Manglende, forkert eller spærret nøgle.
plan_error403Feltet eller endpointet kræver en højere plan. required_plan siger hvilken.
not_found404Kalenderen, lågen eller mediet findes ikke på din konto.
conflict409Den samme idempotency-nøgle blev brugt med en anden body.
validation_error422En værdi er ikke tilladt, for eksempel et datointerval over 50 dage, et lågenummer uden for intervallet eller et billede over 2 MB. field peger på feltet.
rate_limit429For mange forespørgsler. Vent RateLimit-Reset sekunder.
api_error500Fejlen er vores. Prøv igen senere, og send os request_id, hvis det fortsætter.

Grænser

VærdiGrænse
120 / minForespørgsler pr. minut og nøgle. Hvert svar indeholder den aktuelle status i headers.
≤ 50Låger pr. kalender, givet af datointervallet.
2 MBBilledupload: JPG, PNG, GIF, WebP.
20 MBVideoupload: MP4.
50Låger pr. kald af PUT /doors.
24 hIdempotensvindue: hvor længe vi husker en nøgle.
http Rate limit-headers
RateLimit-Limit: 120
RateLimit-Remaining: 117
RateLimit-Reset: 41

Har du brug for mere til en stor kampagne i slutningen af november, så skriv til os, før den starter.

Versionering

Versionen er en del af stien (/v1). Inden for v1 tilføjer vi kun ting: nye endpoints, nye valgfrie felter, nye hændelsestyper. Din kode bør ignorere felter, den ikke kender.

Ændringer, der kan ødelægge integrationer, får en ny datoversion. Du kan låse en version med en header. Uden den bruger din nøgle den version, der var gældende, da den blev udstedt.

http Lås en version
ACM-Version: 2026-09-01

Vi varsler brydende ændringer mindst seks måneder i forvejen på e-mail og udruller dem aldrig mellem 15. november og 6. januar, mens kalendere kører.

Testtilstand

Med en sk_test_-nøgle virker alt som i live-tilstand, bortset fra tre forskelle:

  • Testkalendere har "livemode": false, og deres offentlige link viser et "test"-banner.
  • Testkalendere indekseres ikke af søgemaskiner og slettes efter 30 dage.
  • Du kan sende "simulate_date": "2026-12-07T08:00:00Z" med et GET på en låge eller kalender for at se, hvilke låger der ville være åbne på det tidspunkt.

Webhooks fra testtilstand har "livemode": false, så ét endpoint kan tage imod begge dele.

Privatliv

Dine kalendere og deres indhold tilhører dig. Vi bruger dem kun til at vise kalenderen og aldrig til andet.

  • Besøgende behøver ikke en konto, og vi sætter ingen sporingscookies på offentlige kalendere.
  • Til visninger og åbninger gemmer vi kun en hash af IP-adressen, aldrig selve adressen.
  • Adgangskoder til beskyttede kalendere gemmes kun som hashes.
  • Hosting og databehandling foregår i EU. En databehandleraftale efter GDPR kan fås på forespørgsel.
  • En sporingskode, du selv tilføjer (Enterprise), kører på dit ansvar. Det er din opgave at indhente de besøgendes samtykke til den.

Support

Spørgsmål om API'et, en nøgle eller en webhook sendes til [email protected]. Vedlæg altid request_id fra fejlen eller svar-headerne. Så kan vi finde din forespørgsel på få sekunder.

Enterprise-kunder får prioriteret support, og i ugerne før 1. december besvarer vi API-spørgsmål inden for én hverdag.

Få en API-nøgle

Adgang til API og MCP er inkluderet i Premium (1.499 kr om året) og Enterprise (9.999 kr om året). Skriv til [email protected] med din kontos e-mailadresse, så sender vi dig en testnøgle og en live-nøgle.

Anmod om en nøgle