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.
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.
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.
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
| Metode | Sti | Formål |
|---|---|---|
| POST | /v1/calendars | Opret en kalender. |
| GET | /v1/calendars/{id} | Læs én kalender. |
| GET | /v1/calendars | List 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}/doors | Alle låger i en kalender med åbningstidspunkter og indhold. |
| PUT | /v1/calendars/{id}/doors | Skriv 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/media | Upload et billede, en video, en lydfil eller en download til en låge. |
| GET | /v1/backgrounds | Baggrundsdesigns, skabelonstile og tags. |
| GET | /v1/calendars/{id}/stats | Visninger, lågeåbninger, klik og deltagelser. |
| GET | /v1/calendars/{id}/stats/export | Daglig statistik som CSV (Enterprise). |
| GET | /v1/account | Plan, 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).
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();
| Felt | Type | Beskrivelse |
|---|---|---|
| title | string, required | Vises over kalenderen. Op til 255 tegn. |
| description | string | Kort tekst under titlen. Op til 2000 tegn. |
| start_date | date | Dagen for låge 1. Standard er 1. december. Premium. |
| end_date | date | Dagen for den sidste låge. Intervallet må højst dække 50 dage. Premium. |
| opening_time | HH:MM | Lokal tid, hvor hver låge åbner, for eksempel 08:00. Premium. |
| timezone | string | IANA-tidszonenavn, for eksempel Europe/Copenhagen eller America/New_York. Standard er Europe/Berlin. |
| template_style | string | En af de over 100 lågestile fra /v1/backgrounds. |
| background_image | string | Nøgle til et baggrundsdesign fra kataloget. |
| custom_color | #RRGGBB | Brandfarve som hex-værdi. Premium. |
| door_colors | object | Egne lågefarver oven på den valgte stil: door, accent og number som hex-værdier. null beholder stilens farver. Premium. |
| custom_logo_url | string | HTTPS-URL til dit logo, som vises over lågerne. Premium. |
| animation | string | Baggrundsanimation: none, snow, flurry (tæt snefygning) eller stars (funklende stjerner). Standard er snow. Gratis i alle abonnementer. |
| snow_enabled | boolean | Ældre genvej, true ved snow eller flurry. false sætter animation til none. |
| is_public | boolean | false 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. |
| password | string | Besøgende skal indtaste denne adgangskode, før de ser kalenderen. Gemmes kun som hash. Enterprise. |
| tracking_code | string | Dit eget analyse- eller pixel-snippet, som indlæses på den offentlige side. Enterprise. |
| metadata | object | Op 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
{
"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
}
| Felt | Type | Beskrivelse |
|---|---|---|
| id | string | Stabilt id, brug det i alle videre kald. |
| slug | string | Del 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_url | string | Linket, du deler. Åbner kalenderen på julekalenderskaber.dk. |
| door_count | integer | Antal låger, altid lig med antallet af dage fra start_date til end_date. |
| doors_filled | integer | Hvor mange låger der allerede har indhold. |
| password_protected | boolean | true, hvis der er sat en adgangskode. Selve adgangskoden returneres aldrig. |
| livemode | boolean | false 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.
curl -G https://api.julekalenderskaber.dk/v1/calendars \
-H "Authorization: Bearer $ACM_API_KEY" \
-d active_on=2026-12-10 \
-d limit=50
{
"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.
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.
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"
}'
{
"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:
| Type | Plan | Felter og visning |
|---|---|---|
| text | Free | title 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. |
| embed | Free | url til en YouTube- eller Vimeo-video, vist som indlejret afspiller. |
| link | Premium | url samt valgfri title og text, vist som en knap til din side. data.button bestemmer knappens tekst. |
| image | Premium | media_id for et uploadet billede, valgfri title og text som billedtekst. |
| video | Premium | media_id for en uploadet MP4-video. |
| coupon | Premium | coupon_code samt title og text. Koden vises med en kopiknap. Vi genererer eller validerer aldrig koder. De kommer fra dit shopsystem. |
| quiz | Premium | text 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. |
| product | Premium | title som produktnavn, text som beskrivelse, valgfri media_id for et produktbillede, data.price som visningstekst og url til produktsiden. data.button bestemmer knappens tekst. |
| event | Premium | data.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. |
| giveaway | Premium | data.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. |
| download | Premium | media_id for en uploadet fil samt text. Vises med filnavn og en downloadknap. data.button bestemmer knappens tekst. |
| survey | Premium | text 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. |
| music | Premium | media_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.
{
"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.
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" }'
{
"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.
curl -G https://api.julekalenderskaber.dk/v1/backgrounds \
-H "Authorization: Bearer $ACM_API_KEY" \
-d tag=corporate
{
"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.
{
"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.
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.
{
"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
}
| Funktion | Fra plan | Berører |
|---|---|---|
| Premium | link, image, video, coupon, quiz, product, event, giveaway, download, survey, music | Låger med links, billeder, videoer, rabatkoder, quizzer, produkter, events, konkurrencer, downloads, afstemninger og musik. |
| Premium | text | HTML-formatering i lågetekst. |
| Premium | start_date, end_date, opening_time | Eget datointerval og åbningstidspunkt i stedet for 1. til 24. december ved midnat. |
| Premium | custom_color, door_colors, custom_logo_url | Egen brandfarve, egne lågefarver og eget logo. |
| Premium | /stats | Visninger og åbninger af låger. |
| Enterprise | password | Adgangskodebeskyttede kalendere. |
| Enterprise | tracking_code | Egen sporings- eller pixelkode på den offentlige side. |
| Enterprise | /stats/export | Statistik 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ændelse | Hvornår |
|---|---|
| calendar.created | En kalender blev oprettet, via API eller i editoren. |
| calendar.updated | Indstillinger for en kalender eller indholdet af en låge blev ændret. |
| calendar.deleted | En kalender blev slettet. |
| door.unlocked | En 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. |
{
"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.
ACM-Signature: t=1796626802,v1=9e4b1c7d2a...
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.
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.
{
"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"
}
}
| Type | Status | Betydning |
|---|---|---|
| invalid_request | 400 | Forespørgslen kan ikke læses, for eksempel på grund af ødelagt JSON eller et ukendt felt. |
| authentication_error | 401 | Manglende, forkert eller spærret nøgle. |
| plan_error | 403 | Feltet eller endpointet kræver en højere plan. required_plan siger hvilken. |
| not_found | 404 | Kalenderen, lågen eller mediet findes ikke på din konto. |
| conflict | 409 | Den samme idempotency-nøgle blev brugt med en anden body. |
| validation_error | 422 | En 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_limit | 429 | For mange forespørgsler. Vent RateLimit-Reset sekunder. |
| api_error | 500 | Fejlen er vores. Prøv igen senere, og send os request_id, hvis det fortsætter. |
Grænser
| Værdi | Grænse |
|---|---|
| 120 / min | Forespørgsler pr. minut og nøgle. Hvert svar indeholder den aktuelle status i headers. |
| ≤ 50 | Låger pr. kalender, givet af datointervallet. |
| 2 MB | Billedupload: JPG, PNG, GIF, WebP. |
| 20 MB | Videoupload: MP4. |
| 50 | Låger pr. kald af PUT /doors. |
| 24 h | Idempotensvindue: hvor længe vi husker en nøgle. |
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.
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.