Vejledning

Sådan opsættes et API til Member Import

For at integrere til GoPublic Member Import API, og på den måde kunne eksportere og vedligeholde kontaktoplysninger på jeres medarbejdere, skal I som kunde sikre, at jeres data og systemkonfiguration stemmer overens med følgende krav.

Du kan tillige finde en FAQ nederst på siden.

Endpoint access og request med API-nøgle

Vores API endpoint kan tilgås her: https://gapi.admin.dit-domænenavn.dk/backend/member/import-by-api-key

For at kalde vores Member Import API, skal I bruge en API-nøgle. Denne vil I få fremsendt på mail.

Et request i headeren i fx. Postman vil se ud som følger:

KeyValue
x-api-keyDen fremsendte API-nøgle

Påkrævede felter

uuids

Feltet uuids fungerer som en slags primærnøgle i forbindelse med eksport af members. Det er en unik nøgle, der er påkrævet og bruges til at afgøre, om et “member” (kontaktperson) allerede eksisterer.
Hvis et eksisterende medlem har samme UUID, opdateres det med nye oplysninger. Ellers oprettes et nyt “member”. Det er muligt at tilknytte flere UUIDS til hver “member”.

Det er jer som kunde, der skal danne og medsende dette UUID. Feltet er en string uden krav til opbygning, men et UUID vil typisk være opbygget på følgende måde: f56ac10b-55cc-4372-a567-0e02a3c3d859

name, email, fullName og roles

Felterne name, email, fullName og roles er ligeledes påkrævede.

I skal sørge for, at member rollerne er oprettet i backoffice inden I eksporterer jeres members, da eksporten ellers vil fejle. Såfremt du ikke har adgang til backoffice, skal du henvende dig til en redaktør, for at få member rollerne oprettet.

Læs mere om 'Member Roles' under 'Struktur og feltforklaring' længere nede på siden.

Opdatering af eksisterende members

Sker der ændringer på et eksisterende member, som derfor ønskes opdateret, eksporteres blot den fulde liste inklusive opdateringerne. Vores system sørger for kun at importere de members, der har ændringer i data.

Det er naturligvis også muligt blot at eksportere en liste med de members, der har ændringer, såfremt man ikke ønsker at sende den fulde liste hver gang.

Sletning og Lock-flag

Vi benytter en flag-baseret tilgang til sletning af “members”:

Såfremt et medlem ønskes slettet, sættes feltet lock således: "lock: "true",

Medlemmet vil da blive låst og vil automatisk blive slettet efter 30 dage.

Sendes denne string med i flere gentagne eksporteringer, vil det være den første, der er gældende. 

Påkrævet dataformat

Data skal sendes i følgende struktur:

{

  "members": [

    {

      "uuids": ["string","string"],

      "name": "string",

      "email": "string",

      "firstName": "string",

      "surname": "string",

      "fullName": "string",

      "memberId": "integer",

      "location": "string",

      "dateOfBirth": "2025-12-02T10:31:53.926Z",

      "startDate": "2025-12-02T10:31:53.926Z",

      "title": "string",

      "headOfDepartment": "boolean",

      "department": "string",

      "phoneNumber": "string",

      "mobileNumber": "string",

      "secondaryEmail": "string",

      "workDescription": "string",

      "language": "string",

      "avatar": "string",

      "facebookLink": "string",

      "linkedInLink": "string",

      "instagramLink": "string",

      "xLink": "string",

      "additionalLink": "string",

      "roles": ["string","string"],

      "categories": ["string","string"], 

      "organizations": ["string"],

      "lock": "boolean",

    }

  ]

}

Strukturkrav og feltforklaring

Felterne uuids, roles, categories og organizations skal angives som arrays, da hver medarbejder kan have flere af disse tilknyttet.

categories og organizations skal opbygges med en hierarkisk struktur, skilt med “/”, f.eks. "Org/department/office/3".

Eksempler

Hver “value” er en “string” med hele stien. Alle niveauer i stien tildeles medlemmet automatisk.

Et UUID er en unik nøgle, der benyttes til at identificere et member. Der kan tilknyttes flere UUID til ét Member.

Feltet er en string uden krav til opbygning, men et typisk UUID ser ud som følger: 

"uuids": ["f56ac10b-55cc-4372-a567-0e02a3c3d859","f86ac12c-45cc-4372-a567-0e09b3c7d359"],

roles er et krævet datafelt ved eksport af et member.

Feltet bruges til at styre adgangsrettigheder i GoPublic. For "members" som kun skal vises i telefonbogen, skal der stadig tilknyttes en rolle. Her kan fx følgende rolle benyttes:

  • NoRights

Rollerne i filen skal altid matche rollerne i backoffice.

Hvis nedenstående sendes i filen:

"roles": ["NoRights","LoginOnly","Admin"],

skal backoffice se ud som følger:

Member kategorier kan benyttes til at give oplysninger om den enkelte member. Fx hvilke kurser et member har fuldført, eller om et member skal vises i en telefonbog.

Kategorierne i filen skal altid matche kategorierne i backoffice.

Hvis nedenstående sendes i filen:

"categories": ["Kurser/It-sikkerhed", "Telefonbog/Vis i telefonbog"],

skal backoffice se ud som følger:

Organisations kategorierne benyttes til at placere medlemmet under en bestemt afdeling. 

Kategorierne i filen skal altid matche kategorierne i backoffice.

Hvis nedenstående sendes i filen:

a) "organizations": ["Departement/Styrelse/Afdeling1/Kontor1"],

eller

b) "organizations": ["Departement/Styrelse/Afdeling3"],

skal backoffice se ud som følger:

a)

b)

Feltforklaring

Feltet headOfDepartment er af typen "boolean" og kan udfyldes med enten "true" eller "false". Feltet markeres "true" hvis et member er chef og sikrer at dette member står øverst, hvis en slutbruger filtrere på en afdeling i telefonbogen.

language bruges til at angive et members sprog. Feltet er en "string" og der er derfor blot tale om, at man kan angive sproget (eller nationaliteten) på et member. 

Feltet lock er af typen "boolean" og kan udfyldes med enten "true" eller "false".

Sættes værdien til "true", vil medlemmet blive låst og automatisk blive slettet efter 30 dage.

Felterne name og fullName er begge påkrævet.

Forskellen på de to felter er, at name er det navn, der vil komme til at stå ude i træstrukturen i backoffice, hvor fullName er det navn, der vil stå på medlemmet i telefonbogen.

Datavalidering

Hvis de afsendte data ikke matcher det forventede format, vil API’et returnere en valideringsfejl med information om, hvilke felter der mangler eller er ugyldige.

Frekvens for kald

I vælger selv, hvor ofte der skal foretages kald til endpointet. Hyppigheden for opdatering håndteres på jeres side som del af den service, I har udviklet. 

I GoPublic Member API har vi lavet logik, som sikrer, at kun members med nye oplysninger importeres.

Eksempel på datamapping

Typisk feltnavn hos kundenFeltnavn i GoPublic APIEksempelPåkrævet
UUIDuuids"uuids": ["f56ac10b-55cc-4372-a567-0e02a3c3d859"],X
Navnname"name": "Jens Hansen",X
E-mailemail"email": "jens@testmail.dk",X
FornavnfirstName"firstName": "Jens",
Efternavnsurname"surname": "Hansen",
Fulde navnfullName"fullName": "Jens Søren Hansen",X
Medlems IDmemberId"memberId": "6"
Lokationlocation"location": "København",
FødselsdatodateOfBirth"dateOfBirth": "1974-03-25T10:31:53.926Z",
Start datostartDate"startDate": "2021-03-01T10:31:53.926Z",
Titeltitle"title": "IT-supporter",
AfdelingslederheadOfDepartment"headOfDepartment": "false",
Afdelingdepartment"department": "Support og Service",
TelefonphoneNumber"phoneNumber": "12345678",
MobilmobileNumber"mobileNumber": "87654321",
Alternativ e-mailsecondaryEmail"secondaryEmail": "info@testmail.dk",
ArbejdsbeskrivelseworkDescription"workDescription": "Ansvarlig for IT-supporten af kunderne",
Sproglanguage"language": "Dansk",
Billedeavatar"avatar": "data:image/jpeg;base64,/9j/4AAQ-SkZJRgABAQAAAQABAAD...",
Facebook linkfacebookLink"facebookLink": "https://www.facebook.com/dit.navn",
LinkedIn linklinkedInLink"linkedInLink": "https://www.linkedin.com/in/dit-navn-a3aa1365/",
Instagram linkinstagramLink"instagramLink": "https://www.instagram.com/bruger-navn/",
X linkxLink"xLink": "https://x.com/brugernavn",
Yderligere linkadditionalLink"additionalLink": "https://www.gopublic.dk",
Rollerroles"roles": ["Administrator"],X
Kategoriercategories"categories": ["Kurser/It-sikkerhed", "Kurser/HR-intro"]
Organisationorganizations"organizations": ["GoPublic/Support og Service"],
(Sletning af medlem)lock"lock": "true",

Eksempel på Python og C# script

Vi har opsat disse scripts for at vise, hvordan et fuldt funktionelt Python eller C# script kan se ud.

import csv
import requests

 

API_ENDPOINT = 'https://gapi.admin.domænenavn.dk/backend/member/import-by-api-key'
API_KEY = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
ORIGIN = 'https://domænenavn.dk'
CSV_FILE = "Kontakter.csv"

 

members = []
with open(CSV_FILE, newline='', encoding='utf-8-sig') as f:
reader = csv.DictReader(f, delimiter=';')
for row in reader:
member = {
"uuids": [u.strip() for u in row.get("UUID", "").strip('"').split("|") if u.strip()],
"name": row.get("Name", ""),
"email": row.get("EmailAddress", ""),
"firstName": row.get("FirstName", ""),
"surname": row.get("Surname", ""),
"fullName": row.get("FullName", ""),
"title": row.get("Title", ""),
"headOfDepartment": str(row.get("HeadOfDepartment", "False")).lower() == "true",
"mobileNumber": row.get("MobilePhone", ""),
"location": row.get("Location", ""),
"locked": str(row.get("locked", "False")).lower() == "true",
"roles": [r.strip() for r in row.get("Roles", "").strip('"').split("|") if r.strip()],
"categories": [c.strip() for c in row.get("Categories", "").strip('"').split("|") if c.strip()],
"organizations": [o.strip() for o in row.get("Organizations", "").strip('"').split("|") if o.strip()]
}
members.append(member)

 

headers = {"x-api-key": API_KEY, "Origin": ORIGIN, "Content-Type": "application/json"}
response = requests.post(API_ENDPOINT, json={"members": members}, headers=headers)

 

if response.status_code == 200:
print("Success")
else:
print(f"Error: {response.status_code} - {response.text}")

using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Text;
using System.Text.Json;
using System.Threading.Tasks;

class Program
{
    static async Task Main()
    {
        string API_ENDPOINT = "https://gapi.admin.domænenavn.dk/backend/member/import-by-api-key";
        string API_KEY = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx";
        string ORIGIN = "https://domænenavn.dk";
        string CSV_FILE = "Kontakter.csv";

        var members = new List<Member>();

        var lines = File.ReadAllLines(CSV_FILE, Encoding.UTF8);
        var headers = lines[0].Split(';');

        foreach (var line in lines.Skip(1))
        {
            var values = line.Split(';');
            var row = headers
                .Select((h, i) => new { h, value = i < values.Length ? values[i] : "" })
                .ToDictionary(x => x.h, x => x.value);

            var member = new Member
            {
                uuids = SplitPipe(row.GetValueOrDefault("UUID")),
                name = row.GetValueOrDefault("Name"),
                email = row.GetValueOrDefault("EmailAddress"),
                firstName = row.GetValueOrDefault("FirstName"),
                surname = row.GetValueOrDefault("Surname"),
                fullName = row.GetValueOrDefault("FullName"),
                title = row.GetValueOrDefault("Title"),
                headOfDepartment = ParseBool(row.GetValueOrDefault("HeadOfDepartment")),
                mobileNumber = row.GetValueOrDefault("MobilePhone"),
                location = row.GetValueOrDefault("Location"),
                locked = ParseBool(row.GetValueOrDefault("locked")),
                roles = SplitPipe(row.GetValueOrDefault("Roles")),
                categories = SplitPipe(row.GetValueOrDefault("Categories")),
                organizations = SplitPipe(row.GetValueOrDefault("Organizations"))
            };

            members.Add(member);
        }

        var payload = new { members };

        using var client = new HttpClient();
        client.DefaultRequestHeaders.Add("x-api-key", API_KEY);
        client.DefaultRequestHeaders.Add("Origin", ORIGIN);

        var json = JsonSerializer.Serialize(payload);
        var content = new StringContent(json, Encoding.UTF8, "application/json");

        var response = await client.PostAsync(API_ENDPOINT, content);

        if (response.IsSuccessStatusCode)
        {
            Console.WriteLine("Success");
        }
        else
        {
            var error = await response.Content.ReadAsStringAsync();
            Console.WriteLine($"Error: {(int)response.StatusCode} - {error}");
        }
    }

    static List<string> SplitPipe(string input)
    {
        if (string.IsNullOrWhiteSpace(input))
            return new List<string>();

        return input.Trim('"')
                    .Split('|', StringSplitOptions.RemoveEmptyEntries)
                    .Select(x => x.Trim())
                    .Where(x => !string.IsNullOrWhiteSpace(x))
                    .ToList();
    }

    static bool ParseBool(string value)
    {
        return value?.Trim().ToLower() == "true";
    }
}

public class Member
{
    public List<string> uuids { get; set; }
    public string name { get; set; }
    public string email { get; set; }
    public string firstName { get; set; }
    public string surname { get; set; }
    public string fullName { get; set; }
    public string title { get; set; }
    public bool headOfDepartment { get; set; }
    public string mobileNumber { get; set; }
    public string location { get; set; }
    public bool locked { get; set; }
    public List<string> roles { get; set; }
    public List<string> categories { get; set; }
    public List<string> organizations { get; set; }
}

Member Import FAQ

Her findes svar på de spørgsmål vi har modtaget fra andre kunder. Hvis ikke der findes et svar på det spørgsmål I sidder med, så er I meget velkomne til at skrive til os, så vi kan hjælpe med svar.

Fejl '403 Forbidden' / '403 Access Denied' kommer, når der er en restriktion på sitet, der forhindrer kaldet i at gå igennem fra den IP-adresse du sidder på.

Du skal derfor tjekke din IP-adresse og fremsende den til os, så vi kan whiteliste den.

Hvis importen fejler med beskeden om at rollen ikke er fundet, skal du logge ind i backoffice og tjekke, at rollen er oprettet under 'Member roles'.

Hvis I har f.eks. både et dansk og et engelsk site, og I benytter felter, hvor indhold skal vise på henholdsvis dansk og engelsk, så skal personerne oprettes 

Requestet der sendes til vores API, skal være i JSON-format.

Hvis du får besked om, at navnet allerede findes, skal du logge ind i backoffice og tjekke om personen allerede er oprettet. Tjek også i papirkurven, da eksporten også vil fejle, såfremt personen ligger i papirkurven.

Hvis du får besked om, at email'en allerede er i brug, skal du logge ind i backoffice og tjekke om der findes en person med den fejlede email. Tjek også i papirkurven, da eksporten også vil fejle, såfremt en person med denne email ligger i papirkurven.