diff --git a/frontend/public/locales/en/common.json b/frontend/public/locales/en/common.json
index c1f7e439edae23e44ed6a45de1e1c0fae96c4ad9..2f2f2063e5f4c367f2c3bf0bb2bcbfc0aa2c068f 100644
--- a/frontend/public/locales/en/common.json
+++ b/frontend/public/locales/en/common.json
@@ -89,6 +89,7 @@
     "roleTypeRequired": "Role type is required",
     "roleEndRequired": "Role end date is required",
     "emailRequired": "E-mail is required",
+    "consentRequired": "This consent is required",
     "invalidMobilePhoneNumber": "Invalid phone number",
     "invalidEmail": "Invalid e-mail address",
     "passportNumberRequired": "Passport number required",
diff --git a/frontend/public/locales/nb/common.json b/frontend/public/locales/nb/common.json
index 64ac355125d35d8c40de428d91df23a59ca8e5ce..ec30342da2e1b3dcd488a554c29fe905d8877675 100644
--- a/frontend/public/locales/nb/common.json
+++ b/frontend/public/locales/nb/common.json
@@ -88,6 +88,7 @@
     "roleTypeRequired": "Rolletype er obligatorisk",
     "roleEndRequired": "Sluttdato for rolle er obligatorisk",
     "emailRequired": "E-post er obligatorisk",
+    "consentRequired": "Dette samtykket er obligatorisk",
     "invalidMobilePhoneNumber": "Ugyldig telefonnummer",
     "invalidEmail": "Ugyldig e-postadresse",
     "passportNumberRequired": "Passnummer er obligatorisk",
diff --git a/frontend/public/locales/nn/common.json b/frontend/public/locales/nn/common.json
index 6000ca6ee8d12388338fda29ebddcc6129a9ead7..25d2b543b15648b4977b259b73cddbab5d9d8d44 100644
--- a/frontend/public/locales/nn/common.json
+++ b/frontend/public/locales/nn/common.json
@@ -89,6 +89,7 @@
     "roleTypeRequired": "Rolletype er obligatorisk",
     "roleEndRequired": "Sluttdato for rolle er obligatorisk",
     "emailRequired": "E-post er obligatorisk",
+    "consentRequired": "Dette samtykket er obligatorisk",
     "invalidMobilePhoneNumber": "Ugyldig telefonnummer",
     "invalidEmail": "Ugyldig e-postadresse",
     "passportNumberRequired": "Passnummer er obligatorisk",
diff --git a/frontend/src/components/debug/index.tsx b/frontend/src/components/debug/index.tsx
index c71af5ea554f17960e0dd5f393603742066e7be9..aca2b8bcbe8b9f8fdccb4a23572584ab6131a899 100644
--- a/frontend/src/components/debug/index.tsx
+++ b/frontend/src/components/debug/index.tsx
@@ -17,6 +17,7 @@ import {
 
 import { appInst, appTimezone, appVersion } from 'appConfig'
 import { Link } from 'react-router-dom'
+import { useUserContext } from 'contexts'
 
 const Yes = () => <CheckIcon color="success" />
 const No = () => <ClearIcon color="error" />
@@ -26,8 +27,8 @@ export const Debug = () => {
   const [didContactApi, setDidContactApi] = useState(false)
   const { i18n } = useTranslation(['common'])
   const [csrf, setCsrf] = useState<String | null>(null)
-  const [isAuthenticated, setIsAuthenticated] = useState(false)
   const [username, setUsername] = useState(undefined)
+  const { user } = useUserContext()
 
   if (!didContactApi) {
     setDidContactApi(true)
@@ -70,15 +71,9 @@ export const Debug = () => {
       .then((res) => res.json())
       .then((data) => {
         console.log(data)
-        if (data.isAuthenticated) {
-          setIsAuthenticated(true)
-        } else {
-          setIsAuthenticated(false)
-          getCSRF()
-        }
+        getCSRF()
       })
       .catch((err) => {
-        setIsAuthenticated(false)
         console.log(err)
       })
   }
@@ -126,7 +121,6 @@ export const Debug = () => {
       .then(isResponseOk)
       .then((data) => {
         console.log(data)
-        setIsAuthenticated(false)
         getCSRF()
       })
       .catch((err) => {
@@ -142,7 +136,7 @@ export const Debug = () => {
     ['Theme', appInst],
     ['Institution', appInst],
     ['API reachable?', apiHealth === 'yes' ? <Yes /> : apiHealth],
-    ['Authenticated?', isAuthenticated ? <Yes /> : <No />],
+    ['Authenticated?', user.auth ? <Yes /> : <No />],
     ['Username', username],
     ['CSRF', csrf],
   ]
diff --git a/frontend/src/hooks/useConsentTypes/index.ts b/frontend/src/hooks/useConsentTypes/index.ts
new file mode 100644
index 0000000000000000000000000000000000000000..597bb716184ac1eb866b8c6fe68be1cec4048ec3
--- /dev/null
+++ b/frontend/src/hooks/useConsentTypes/index.ts
@@ -0,0 +1,49 @@
+import { useState, useEffect } from 'react'
+import { fetchJsonOpts } from 'utils'
+
+type ConsentChoice = {
+    id?: number
+    value: string
+    text_en: string
+    text_nb: string
+    text_nn: string
+}
+
+type ConsentType = {
+  id: number
+  identifier: string
+  name_en: string
+  name_nb: string
+  name_nn: string
+  description_en: string
+  description_nb: string
+  description_nn: string
+  mandatory: boolean
+  user_allowed_to_change: boolean
+  valid_from: string
+  choices: ConsentChoice[]
+}
+
+export function useConsentTypes(): ConsentType[] {
+  const [consentTypes, setConsentTypes] = useState<ConsentType[]>([])
+
+  async function fetchConsentTypes() {
+    fetch(`/api/ui/v1/consenttypes`, fetchJsonOpts())
+      .then((data) => data.text())
+      .then((result) => {
+        setConsentTypes(JSON.parse(result))
+      })
+      .catch((error) => {
+        console.error(error)
+      })
+  }
+
+  useEffect(() => {
+    fetchConsentTypes()
+  }, [])
+
+  return consentTypes
+}
+
+export type { ConsentType, ConsentChoice }
+export default useConsentTypes
diff --git a/frontend/src/hooks/useOus/index.tsx b/frontend/src/hooks/useOus/index.tsx
index b8073e4e14c1263f6f5858712e41b1a72f433458..0109ec3bdf853e2930c2598b5a96155acd89428d 100644
--- a/frontend/src/hooks/useOus/index.tsx
+++ b/frontend/src/hooks/useOus/index.tsx
@@ -1,4 +1,5 @@
 import { useState, useEffect } from 'react'
+import { fetchJsonOpts } from 'utils'
 
 type OuData = {
   id: number
@@ -9,7 +10,7 @@ type OuData = {
 function useOus(): OuData[] {
   const [ous, setOus] = useState<OuData[]>([])
   const getOptions = async () => {
-    const response = await fetch('/api/ui/v1/ous?format=json')
+    const response = await fetch('/api/ui/v1/ous/', fetchJsonOpts())
     if (response.ok) {
       const ousJson = await response.json()
       setOus(ousJson)
diff --git a/frontend/src/routes/guest/register/enteredGuestData.ts b/frontend/src/routes/guest/register/enteredGuestData.ts
index 45422db11b05ea077d3b062dbb537e852ec64a86..77a914d905ba3177401c0af05c8d89793cd9139e 100644
--- a/frontend/src/routes/guest/register/enteredGuestData.ts
+++ b/frontend/src/routes/guest/register/enteredGuestData.ts
@@ -3,7 +3,7 @@
  * separate from ContactInformationBySponsor to make it clear that
  * most of the data there the guest cannot change.
  */
-export type EnteredGuestData = {
+export type GuestRegisterData = {
   firstName: string
   lastName: string
   mobilePhoneCountry: string
@@ -13,3 +13,10 @@ export type EnteredGuestData = {
   passportNationality: string
   dateOfBirth?: Date
 }
+
+export type GuestConsentData = {
+  consents?: Array<{
+    type: string
+    choice: string
+  }>
+}
diff --git a/frontend/src/routes/guest/register/index.tsx b/frontend/src/routes/guest/register/index.tsx
index 712e30e2d1f229d4c6a177571e3b121e90e23733..e5fe322316c20f0c7a8677a5b5214b727cc1436d 100644
--- a/frontend/src/routes/guest/register/index.tsx
+++ b/frontend/src/routes/guest/register/index.tsx
@@ -1,7 +1,7 @@
-import React, { useEffect, useRef, useState } from 'react'
+import React, { Suspense, useEffect, useRef, useState } from 'react'
 import { useTranslation } from 'react-i18next'
 
-import { Box, Button } from '@mui/material'
+import { Box, Button, CircularProgress } from '@mui/material'
 import Page from 'components/page'
 
 import { useHistory } from 'react-router-dom'
@@ -11,14 +11,15 @@ import {
   getCountryCallingCode,
 } from 'libphonenumber-js'
 import format from 'date-fns/format'
+import { splitPhoneNumber, submitJsonOpts, fetchJsonOpts } from 'utils'
 import OverviewGuestButton from '../../components/overviewGuestButton'
-import GuestRegisterStep from './registerPage'
 import { GuestRegisterCallableMethods } from './guestRegisterCallableMethods'
-import { EnteredGuestData } from './enteredGuestData'
+import { GuestRegisterData, GuestConsentData } from './enteredGuestData'
 import { GuestInviteInformation } from './guestDataForm'
 import AuthenticationMethod from './authenticationMethod'
-import { splitPhoneNumber, submitJsonOpts } from '../../../utils'
-import StepSubmitSuccessGuest from './submitSuccessPage'
+import GuestRegisterStep from './steps/register'
+import GuestConsentStep from './steps/consent'
+import GuestSuccessStep from './steps/success'
 
 enum SubmitState {
   NotSubmitted,
@@ -27,8 +28,37 @@ enum SubmitState {
 }
 
 enum Step {
-  RegisterStep = 0,
-  SubmitSuccessStep = 1,
+  RegisterStep,
+  ConsentStep,
+  SuccessStep,
+}
+
+type InvitationData = {
+  person: {
+    first_name: string
+    last_name: string
+    email?: string
+    mobile_phone?: string
+    fnr?: string
+    passport?: string
+    feide_id?: string
+  }
+  sponsor: {
+    first_name: string
+    last_name: string
+  }
+  role: {
+    ou_name_nb: string
+    ou_name_en: string
+    role_name_nb: string
+    role_name_en: string
+    start: string
+    end: string
+    comments: string
+  }
+  meta: {
+    session_type: string
+  }
 }
 
 /*
@@ -44,157 +74,172 @@ export default function GuestRegister() {
   )
   const guestRegisterRef = useRef<GuestRegisterCallableMethods>(null)
   // TODO Set step when user moves between pages
-  // eslint-disable-next-line @typescript-eslint/no-unused-vars
   const [activeStep, setActiveStep] = useState(0)
 
-  const [guestFormData, setGuestFormData] = useState<GuestInviteInformation>({
-    first_name: '',
-    last_name: '',
-    ou_name_en: '',
-    ou_name_nb: '',
-    role_name_en: '',
-    role_name_nb: '',
-    role_start: '',
-    role_end: '',
-    comment: '',
-    email: '',
-    mobile_phone: '',
-    fnr: '',
-    passport: '',
-    passportNationality: '',
-    countryForCallingCode: '',
-    dateOfBirth: undefined,
-    authentication_method: AuthenticationMethod.Invite,
-  })
-
-  const guestContactInfo = async () => {
-    try {
-      const response = await fetch('/api/ui/v1/invited/')
-
-      if (response.ok) {
-        response.json().then((jsonResponse) => {
-          const authenticationMethod =
-            jsonResponse.meta.session_type === 'invite'
-              ? AuthenticationMethod.Invite
-              : AuthenticationMethod.Feide
-
-          const [countryCode, nationalNumber] = jsonResponse.person.mobile_phone
-            ? splitPhoneNumber(jsonResponse.person.mobile_phone)
-            : ['', '']
-
-          let extractedCountryCode = ''
-          if (countryCode) {
-            const matchingCountries = getCountries().find(
-              (value) => getCountryCallingCode(value) === countryCode
-            )
-
-            if (matchingCountries && matchingCountries.length > 0) {
-              extractedCountryCode = matchingCountries.toString()
-            }
-          }
-
-          let passportNumber = ''
-          let passportCountry = ''
-          if (jsonResponse.person.passport) {
-            const index = jsonResponse.person.passport.indexOf('-')
-
-            if (index !== -1) {
-              passportCountry = jsonResponse.person.passport.substring(0, index)
-              passportNumber = jsonResponse.person.passport.substring(
-                index + 1,
-                jsonResponse.person.passport.length
-              )
-            }
-          }
-
-          setGuestFormData({
-            fnr: jsonResponse.person.fnr,
-            passportNationality: passportCountry,
-            passport: passportNumber,
-
-            first_name: jsonResponse.person.first_name,
-            last_name: jsonResponse.person.last_name,
-            ou_name_en: jsonResponse.role.ou_name_en,
-            ou_name_nb: jsonResponse.role.ou_name_nb,
-            role_name_en: jsonResponse.role.role_name_en,
-            role_name_nb: jsonResponse.role.role_name_nb,
-            role_start: jsonResponse.role.start,
-            role_end: jsonResponse.role.end,
-            comment: jsonResponse.role.comments,
-
-            email: jsonResponse.person.email,
-            feide_id: jsonResponse.person.feide_id,
-            mobile_phone_country_code: countryCode,
-            mobile_phone: nationalNumber,
-            countryForCallingCode: extractedCountryCode,
-
-            dateOfBirth: jsonResponse.person.date_of_birth,
-
-            authentication_method: authenticationMethod,
-          })
-        })
+  const [initialGuestData, setInitialGuestData] =
+    useState<GuestInviteInformation>({
+      first_name: '',
+      last_name: '',
+      ou_name_en: '',
+      ou_name_nb: '',
+      role_name_en: '',
+      role_name_nb: '',
+      role_start: '',
+      role_end: '',
+      comment: '',
+      email: '',
+      mobile_phone: '',
+      dateOfBirth: undefined,
+      fnr: '',
+      passport: '',
+      passportNationality: '',
+      countryForCallingCode: '',
+      authentication_method: AuthenticationMethod.Invite,
+    })
+
+  const [guestRegisterData, setGuestRegisterData] =
+    useState<GuestRegisterData | null>(null)
+  const [guestConsentData, setGuestConsentData] =
+    useState<GuestConsentData | null>(null)
+
+  const fetchInvitationData = async () => {
+    const response = await fetch('/api/ui/v1/invited/', fetchJsonOpts())
+    if (!response.ok) {
+      return
+    }
+    const data: InvitationData = await response.json()
+
+    const authenticationMethod =
+      data.meta.session_type === 'invite'
+        ? AuthenticationMethod.Invite
+        : AuthenticationMethod.Feide
+
+    const [countryCode, nationalNumber] = data.person.mobile_phone
+      ? splitPhoneNumber(data.person.mobile_phone)
+      : ['', '']
+
+    let extractedCountryCode = ''
+    if (countryCode) {
+      const matchingCountries = getCountries().find(
+        (value) => getCountryCallingCode(value) === countryCode
+      )
+
+      if (matchingCountries && matchingCountries.length > 0) {
+        extractedCountryCode = matchingCountries.toString()
+      }
+    }
+
+    let passportNumber = ''
+    let passportNationality = ''
+    if (data.person.passport) {
+      const parts = data.person.passport.split('-', 1)
+      if (parts.length === 2) {
+        ;[passportNationality, passportNumber] = parts
       }
-    } catch (error) {
-      console.log(error)
     }
+
+    setInitialGuestData({
+      first_name: data.person.first_name,
+      last_name: data.person.last_name,
+      ou_name_en: data.role.ou_name_en,
+      ou_name_nb: data.role.ou_name_nb,
+      role_name_en: data.role.role_name_en,
+      role_name_nb: data.role.role_name_nb,
+      role_start: data.role.start,
+      role_end: data.role.end,
+      comment: data.role.comments,
+
+      email: data.person.email,
+      feide_id: data.person.feide_id,
+      fnr: data.person.fnr,
+
+      passport: passportNumber,
+      passportNationality,
+
+      mobile_phone_country_code: countryCode,
+      mobile_phone: nationalNumber,
+      countryForCallingCode: extractedCountryCode,
+
+      authentication_method: authenticationMethod,
+    })
   }
 
   useEffect(() => {
-    guestContactInfo()
+    fetchInvitationData()
   }, [])
 
   const handleNext = () => {
-    // TODO Go to consent page
-    // setActiveStep((prevActiveStep) => prevActiveStep + 1)
-  }
-
-  const handleSave = () => {
-    if (activeStep === 0) {
+    if (activeStep === Step.RegisterStep) {
       if (guestRegisterRef.current) {
         guestRegisterRef.current.doSubmit()
       }
     }
   }
 
-  const handleForwardFromRegister = (
-    updateFormData: EnteredGuestData
-  ): void => {
-    // TODO Should go to consent page here, if there are consents defined in the database. Submit should happen after after consent page
+  const handleBack = () => {
+    if (activeStep === Step.ConsentStep) {
+      setActiveStep(Step.RegisterStep)
+    }
+  }
 
-    // Only add fields to the objects that the user can change (this is also checked on the server side)
-    const payload: any = {}
-    payload.person = {}
+  const makePayload = (
+    registerData: GuestRegisterData,
+    consentData: GuestConsentData
+  ): any => {
+    const payload: any = {
+      person: {},
+    }
     payload.person.mobile_phone = `+${getCountryCallingCode(
-      updateFormData.mobilePhoneCountry as CountryCode
-    )}${updateFormData.mobilePhone}`
+      registerData.mobilePhoneCountry as CountryCode
+    )}${registerData.mobilePhone}`
 
-    if (guestFormData.authentication_method === AuthenticationMethod.Invite) {
+    if (
+      initialGuestData.authentication_method === AuthenticationMethod.Invite
+    ) {
       // The authentication method is Invite, so the name does not come from a
       // trusted third-party source, and the user can update it
-      payload.person.first_name = updateFormData.firstName
-      payload.person.last_name = updateFormData.lastName
+      payload.person.first_name = registerData.firstName
+      payload.person.last_name = registerData.lastName
+    }
+
+    if (registerData.passportNumber && registerData.passportNationality) {
+      payload.person.passport = `${registerData.passportNationality}-${registerData.passportNumber}`
     }
 
-    if (updateFormData.passportNumber && updateFormData.passportNationality) {
-      payload.person.passport = `${updateFormData.passportNationality}-${updateFormData.passportNumber}`
+    if (registerData.nationalIdNumber) {
+      payload.person.fnr = registerData.nationalIdNumber
     }
 
-    if (updateFormData.nationalIdNumber) {
-      payload.person.fnr = updateFormData.nationalIdNumber
+    if (consentData.consents) {
+      payload.person.consents = consentData.consents
     }
 
-    if (updateFormData.dateOfBirth) {
+    if (registerData.dateOfBirth) {
       payload.person.date_of_birth = format(
-        updateFormData.dateOfBirth as Date,
+        registerData.dateOfBirth as Date,
         'yyyy-MM-dd'
       )
     }
 
+    return payload
+  }
+
+  const submitPayload = () => {
+    if (!guestRegisterData) {
+      setActiveStep(Step.RegisterStep)
+      return
+    }
+    if (!guestConsentData) {
+      setActiveStep(Step.ConsentStep)
+      return
+    }
+    const payload: any = makePayload(guestRegisterData, guestConsentData)
+    console.log('submitting payload', payload)
     fetch('/api/ui/v1/invited/', submitJsonOpts('POST', payload))
       .then((response) => {
         if (response.ok) {
           setSubmitState(SubmitState.Submitted)
-          setActiveStep(Step.SubmitSuccessStep)
+          setActiveStep(Step.SuccessStep)
         } else {
           setSubmitState(SubmitState.SubmittedError)
           console.error(`Server responded with status: ${response.status}`)
@@ -206,6 +251,27 @@ export default function GuestRegister() {
       })
   }
 
+  const handleForwardFromRegister = (registerData: GuestRegisterData): void => {
+    console.log('handleForwardFromRegister')
+    setGuestRegisterData(registerData)
+    setActiveStep(Step.ConsentStep)
+  }
+
+  const handleForwardFromConsent = (consentData: GuestConsentData): void => {
+    console.log('handleForwardFromConsent')
+    setGuestConsentData(consentData)
+    if (!guestRegisterData) {
+      setActiveStep(Step.RegisterStep)
+      return
+    }
+
+    if (!guestConsentData) {
+      setActiveStep(Step.ConsentStep)
+      return
+    }
+    submitPayload()
+  }
+
   const handleCancel = () => {
     history.push('/')
   }
@@ -213,17 +279,34 @@ export default function GuestRegister() {
   return (
     <Page>
       <OverviewGuestButton />
-      {/* Current page in wizard */}
-      <Box sx={{ width: '100%' }}>
-        {activeStep === Step.RegisterStep && (
+      {/* Step: Registration  */}
+      {activeStep === Step.RegisterStep && (
+        <Box sx={{ width: '100%' }}>
           <GuestRegisterStep
             nextHandler={handleForwardFromRegister}
-            guestData={guestFormData}
+            initialGuestData={initialGuestData}
+            registerData={guestRegisterData}
             ref={guestRegisterRef}
           />
-        )}
-      </Box>
-
+        </Box>
+      )}
+
+      {/* Step: Consent */}
+      {activeStep === Step.ConsentStep && (
+        <Box sx={{ width: '100%' }}>
+          <Suspense fallback={<CircularProgress />}>
+            <GuestConsentStep
+              nextHandler={handleForwardFromConsent}
+              ref={guestRegisterRef}
+            />
+          </Suspense>
+        </Box>
+      )}
+
+      {/* Step: Success */}
+      {activeStep === Step.SuccessStep && <GuestSuccessStep />}
+
+      {/* Navigation */}
       <Box
         sx={{
           display: 'flex',
@@ -243,20 +326,22 @@ export default function GuestRegister() {
             {t('button.next')}
           </Button>
         )}
+        {activeStep === Step.ConsentStep && (
+          <Button
+            data-testid="button-black"
+            sx={{ color: 'theme.palette.secondary', mr: 1 }}
+            onClick={handleBack}
+          >
+            {t('button.back')}
+          </Button>
+        )}
 
-        {activeStep !== Step.SubmitSuccessStep && (
-          <>
-            <Button color="secondary" onClick={handleCancel}>
-              {t('button.cancel')}
-            </Button>
-
-            {/* TODO This button is only for testing while developing */}
-            <Button onClick={handleSave}>{t('button.save')}</Button>
-          </>
+        {activeStep !== Step.SuccessStep && (
+          <Button color="secondary" onClick={handleCancel}>
+            {t('button.cancel')}
+          </Button>
         )}
       </Box>
-
-      {activeStep === Step.SubmitSuccessStep && <StepSubmitSuccessGuest />}
     </Page>
   )
 }
diff --git a/frontend/src/routes/guest/register/steps/consent.tsx b/frontend/src/routes/guest/register/steps/consent.tsx
new file mode 100644
index 0000000000000000000000000000000000000000..cf2ad31ecd5208c278ef9f321c6a51a085705ed9
--- /dev/null
+++ b/frontend/src/routes/guest/register/steps/consent.tsx
@@ -0,0 +1,177 @@
+import { forwardRef, Ref, useImperativeHandle } from 'react'
+import { SubmitHandler, useForm, useFieldArray } from 'react-hook-form'
+
+import { useTranslation } from 'react-i18next'
+import {
+  Box,
+  Button,
+  FormControl,
+  FormGroup,
+  FormHelperText,
+  FormLabel,
+  FormControlLabel,
+  Checkbox,
+  Radio,
+  RadioGroup,
+  Theme,
+  Typography,
+} from '@mui/material'
+import { ConsentType, useConsentTypes } from 'hooks/useConsentTypes'
+import { getLocalized } from 'utils'
+import { GuestConsentData } from '../enteredGuestData'
+import { GuestRegisterCallableMethods } from '../guestRegisterCallableMethods'
+
+interface GuestConsentProps {
+  nextHandler(consentData: GuestConsentData): void
+}
+
+const GuestConsentStep = forwardRef(
+  (props: GuestConsentProps, ref: Ref<GuestRegisterCallableMethods>) => {
+    const { i18n, t } = useTranslation(['common'])
+    const { nextHandler } = props
+    const consentTypes = useConsentTypes()
+    const {
+      register,
+      control,
+      handleSubmit,
+      formState: { errors },
+    } = useForm<GuestConsentData>()
+    useFieldArray({ control, name: 'consents' })
+
+    const submit: SubmitHandler<GuestConsentData> = (data) => {
+      console.log('consent submit', data)
+
+      nextHandler(data)
+    }
+
+    const onSubmit = handleSubmit<GuestConsentData>(submit)
+    useImperativeHandle(ref, () => ({ doSubmit: () => onSubmit() }))
+
+    console.log('errors', errors)
+
+    function showChoices(
+      consentIndex: number,
+      consentType: ConsentType,
+      name: string,
+      description: string
+    ) {
+      const { choices } = consentType
+      const hasError =
+        errors && errors.consents && !!errors.consents[consentIndex]
+
+      const header = (
+        <>
+          <FormLabel
+            sx={{
+              fontSize: '1.25rem',
+              fontWeight: '500',
+              color: (theme: Theme) =>
+                hasError ? theme.palette.error.main : 'black',
+              paddingTop: '1.25rem',
+              paddingBottom: '1rem',
+            }}
+          >
+            {name}
+          </FormLabel>
+          <Typography
+            sx={{ fontWeight: 600 }}
+            dangerouslySetInnerHTML={{ __html: description }}
+          />
+        </>
+      )
+
+      const showError = (text: string) => (
+        <FormHelperText sx={{ marginLeft: 0 }}>{text}</FormHelperText>
+      )
+
+      if (choices.length === 1) {
+        return (
+          <FormControl component="fieldset" error={hasError}>
+            {header}
+            <FormGroup>
+              <input
+                type="hidden"
+                value={consentType.identifier}
+                {...register(`consents.${consentIndex}.type` as const)}
+              />
+              {choices.map((choice) => {
+                const choiceText = getLocalized(choice, 'text_', i18n.language)
+                return (
+                  <FormControlLabel
+                    key={choice.value}
+                    control={<Checkbox />}
+                    label={choiceText}
+                    value={choice.value}
+                    {...register(`consents.${consentIndex}.choice` as const, {
+                      required: consentType.mandatory,
+                    })}
+                  />
+                )
+              })}{' '}
+            </FormGroup>
+            {hasError && showError(t('validation.consentRequired'))}
+          </FormControl>
+        )
+      }
+      return (
+        <FormControl component="fieldset" error={hasError}>
+          {header}
+
+          <RadioGroup>
+            <input
+              type="hidden"
+              value={consentType.identifier}
+              {...register(`consents.${consentIndex}.type` as const)}
+            />
+            {choices.map((choice) => {
+              const choiceText = getLocalized(choice, 'text_', i18n.language)
+              return (
+                <FormControlLabel
+                  key={choice.value}
+                  value={choice.value}
+                  control={<Radio />}
+                  label={choiceText}
+                  {...register(`consents.${consentIndex}.choice` as const, {
+                    required: consentType.mandatory,
+                  })}
+                />
+              )
+            })}
+          </RadioGroup>
+          {hasError && showError(t('validation.consentRequired'))}
+        </FormControl>
+      )
+    }
+
+    return (
+      <Box>
+        <form onSubmit={onSubmit}>
+          {consentTypes.map((consentType, consentIndex) => {
+            const name =
+              getLocalized(consentType, 'name_', i18n.language) ||
+              consentType.identifier
+            const description =
+              getLocalized(consentType, 'description_', i18n.language) || ''
+            return (
+              <Box
+                sx={{
+                  borderRadius: '4px',
+                  borderStyle: 'solid',
+                  borderWidth: '1px',
+                  borderColor: (theme: Theme) => theme.palette.text.primary,
+                  padding: '0 1.5rem 1.2rem 1.5rem',
+                  marginBottom: '2.5rem',
+                }}
+              >
+                {showChoices(consentIndex, consentType, name, description)}
+              </Box>
+            )
+          })}
+          <Button onClick={onSubmit}>{t('button.save')}</Button>
+        </form>
+      </Box>
+    )
+  }
+)
+
+export default GuestConsentStep
diff --git a/frontend/src/routes/guest/register/registerPage.test.tsx b/frontend/src/routes/guest/register/steps/register.test.tsx
similarity index 80%
rename from frontend/src/routes/guest/register/registerPage.test.tsx
rename to frontend/src/routes/guest/register/steps/register.test.tsx
index 634f2467b358f2858827e208a8c437f4fa8ec2dd..13c7d35cbf729c62a723b808390261c8851ff7c2 100644
--- a/frontend/src/routes/guest/register/registerPage.test.tsx
+++ b/frontend/src/routes/guest/register/steps/register.test.tsx
@@ -2,10 +2,10 @@ import React from 'react'
 import { render, screen, waitFor } from 'test-utils'
 import AdapterDateFns from '@mui/lab/AdapterDateFns'
 import { LocalizationProvider } from '@mui/lab'
-import GuestRegisterStep from './registerPage'
-import { EnteredGuestData } from './enteredGuestData'
-import AuthenticationMethod from './authenticationMethod'
-import { GuestInviteInformation } from './guestDataForm'
+import GuestRegisterStep from './register'
+import { EnteredGuestData } from '../enteredGuestData'
+import AuthenticationMethod from '../authenticationMethod'
+import { GuestInviteInformation } from '../guestDataForm'
 
 function getEmptyGuestData(): GuestInviteInformation {
   return {
@@ -36,7 +36,7 @@ test('Guest register page showing passport field on manual registration', async
     <LocalizationProvider dateAdapter={AdapterDateFns}>
       <GuestRegisterStep
         nextHandler={nextHandler}
-        guestData={getEmptyGuestData()}
+        initialGuestData={getEmptyGuestData()}
       />
     </LocalizationProvider>
   )
diff --git a/frontend/src/routes/guest/register/registerPage.tsx b/frontend/src/routes/guest/register/steps/register.tsx
similarity index 84%
rename from frontend/src/routes/guest/register/registerPage.tsx
rename to frontend/src/routes/guest/register/steps/register.tsx
index 04e55fe60f96da84e105a78546e3d8440f3d0165..60fad8ee988987abc80d08b930995423b5e68025 100644
--- a/frontend/src/routes/guest/register/registerPage.tsx
+++ b/frontend/src/routes/guest/register/steps/register.tsx
@@ -25,16 +25,16 @@ import {
 import { getAlpha2Codes, getName } from 'i18n-iso-countries'
 import { DatePicker } from '@mui/lab'
 import { subYears } from 'date-fns/fp'
-import { GuestInviteInformation } from './guestDataForm'
-import { EnteredGuestData } from './enteredGuestData'
-import { GuestRegisterCallableMethods } from './guestRegisterCallableMethods'
-import { isValidFnr, isValidMobilePhoneNumber } from '../../../utils'
-import AuthenticationMethod from './authenticationMethod'
+import { isValidFnr, isValidMobilePhoneNumber } from 'utils'
+import { GuestInviteInformation } from '../guestDataForm'
+import { GuestRegisterData } from '../enteredGuestData'
+import { GuestRegisterCallableMethods } from '../guestRegisterCallableMethods'
+import AuthenticationMethod from '../authenticationMethod'
 
 interface GuestRegisterProperties {
-  nextHandler(guestData: EnteredGuestData): void
-
-  guestData: GuestInviteInformation
+  nextHandler(registerData: GuestRegisterData): void
+  initialGuestData: GuestInviteInformation
+  registerData: GuestRegisterData | null
 }
 
 /**
@@ -45,7 +45,7 @@ interface GuestRegisterProperties {
 const GuestRegisterStep = forwardRef(
   (props: GuestRegisterProperties, ref: Ref<GuestRegisterCallableMethods>) => {
     const { i18n, t } = useTranslation(['common'])
-    const { nextHandler, guestData } = props
+    const { nextHandler, initialGuestData, registerData } = props
 
     // For select-components it seems to be easier to tie them to a state
     // and then handle the updating of the form using this, than to tie the
@@ -58,7 +58,23 @@ const GuestRegisterStep = forwardRef(
     >(undefined)
     const [idErrorState, setIdErrorState] = useState<string>('')
 
-    const submit: SubmitHandler<EnteredGuestData> = (data) => {
+    const {
+      register,
+      handleSubmit,
+      setValue,
+      setError,
+      clearErrors,
+      control,
+      trigger,
+      formState: { errors },
+    } = useForm<GuestRegisterData>({
+      defaultValues: registerData ?? {},
+    })
+
+    const submit: SubmitHandler<GuestRegisterData> = async (data) => {
+      console.log('submit data is', data)
+      const result = await trigger()
+      console.log('trigger result is', result)
       if (
         !data.nationalIdNumber &&
         !data.passportNumber &&
@@ -70,7 +86,7 @@ const GuestRegisterStep = forwardRef(
         return
       }
 
-      // The user has entered some passport information, check that both nationality and number are present
+      // if one on the passport fields are set, check that both are set
       if (
         (data.passportNumber && !data.passportNationality) ||
         (!data.passportNumber && data.passportNationality)
@@ -80,19 +96,13 @@ const GuestRegisterStep = forwardRef(
       }
       setIdErrorState('')
 
-      nextHandler(data)
-    }
+      console.log('register submit errors', errors)
 
-    const {
-      register,
-      handleSubmit,
-      setValue,
-      setError,
-      clearErrors,
-      control,
-      formState: { errors },
-    } = useForm<EnteredGuestData>()
-    const onSubmit = handleSubmit<EnteredGuestData>(submit)
+      if (!Object.keys(errors).length) {
+        nextHandler(data)
+      }
+    }
+    const onSubmit = handleSubmit<GuestRegisterData>(submit)
 
     const handlePassportNationalityChange = (event: SelectChangeEvent) => {
       if (event.target.value) {
@@ -147,46 +157,44 @@ const GuestRegisterStep = forwardRef(
       // Take values coming from the server, if present, and insert them into the form.
       // This effect has guestData as a dependency, so the data will be reloaded
       // if guestData changes
-      setValue('firstName', guestData.first_name)
-      setValue('lastName', guestData.last_name)
+      setValue('firstName', initialGuestData.first_name)
+      setValue('lastName', initialGuestData.last_name)
       setValue(
         'mobilePhone',
-        guestData.mobile_phone ? guestData.mobile_phone : ''
+        registerData?.mobilePhone ?? (initialGuestData.mobile_phone || '')
       )
 
       setValue(
         'nationalIdNumber',
-        guestData.fnr === undefined ? '' : guestData.fnr
+        initialGuestData.fnr === undefined ? '' : initialGuestData.fnr
       )
       setValue(
         'passportNationality',
-        guestData.passportNationality === undefined
+        initialGuestData.passportNationality === undefined
           ? ''
-          : guestData.passportNationality
+          : initialGuestData.passportNationality
       )
-      setPassportNationality(guestData.passportNationality)
+      setPassportNationality(initialGuestData.passportNationality)
       setValue(
         'passportNumber',
-        guestData.passport === undefined ? '' : guestData.passport
+        initialGuestData.passport === undefined ? '' : initialGuestData.passport
       )
 
-      setCountryCode(guestData.countryForCallingCode)
+      setCountryCode(initialGuestData.countryForCallingCode)
       setValue(
         'mobilePhoneCountry',
-        guestData.countryForCallingCode ? guestData.countryForCallingCode : ''
+        initialGuestData.countryForCallingCode
+          ? initialGuestData.countryForCallingCode
+          : ''
       )
 
-      setValue('dateOfBirth', guestData.dateOfBirth)
-    }, [guestData])
-
-    function doSubmit() {
-      return onSubmit()
-    }
+      setValue('dateOfBirth', initialGuestData.dateOfBirth)
+    }, [initialGuestData])
 
     register('mobilePhoneCountry')
     register('passportNationality')
 
-    useImperativeHandle(ref, () => ({ doSubmit }))
+    useImperativeHandle(ref, () => ({ doSubmit: () => onSubmit() }))
 
     return (
       <>
@@ -206,19 +214,19 @@ const GuestRegisterStep = forwardRef(
           <form onSubmit={onSubmit}>
             <Stack spacing={2}>
               {/* The name is only editable if it is it is not coming from some trusted source */}
-              {guestData.authentication_method !==
+              {initialGuestData.authentication_method !==
               AuthenticationMethod.Invite ? (
                 <>
                   <TextField
                     id="firstName"
                     label={t('input.firstName')}
-                    value={guestData.first_name}
+                    // value={initialGuestData.first_name}
                     disabled
                   />
                   <TextField
                     id="lastName"
                     label={t('input.lastName')}
-                    value={guestData.last_name}
+                    value={initialGuestData.last_name}
                     disabled
                   />
                 </>
@@ -229,7 +237,9 @@ const GuestRegisterStep = forwardRef(
                     control={control}
                     defaultValue=""
                     rules={{
-                      required: true,
+                      required: t(
+                        'common:validation.firstNameRequired'
+                      ).toString(),
                     }}
                     render={({ field: { onChange, value } }) => (
                       <TextField
@@ -237,6 +247,10 @@ const GuestRegisterStep = forwardRef(
                         label={t('input.firstName')}
                         value={value}
                         onChange={onChange}
+                        error={!!errors.firstName}
+                        helperText={
+                          errors.firstName && errors.firstName.message
+                        }
                       />
                     )}
                   />
@@ -245,7 +259,9 @@ const GuestRegisterStep = forwardRef(
                     control={control}
                     defaultValue=""
                     rules={{
-                      required: true,
+                      required: t(
+                        'common:validation.lastNameRequired'
+                      ).toString(),
                     }}
                     render={({ field: { onChange, value } }) => (
                       <TextField
@@ -253,6 +269,8 @@ const GuestRegisterStep = forwardRef(
                         label={t('input.lastName')}
                         value={value}
                         onChange={onChange}
+                        error={!!errors.lastName}
+                        helperText={errors.lastName && errors.lastName.message}
                       />
                     )}
                   />
@@ -287,16 +305,16 @@ const GuestRegisterStep = forwardRef(
               <TextField
                 id="email"
                 label={t('input.email')}
-                value={!guestData.email ? '' : guestData.email}
+                value={initialGuestData.email || ''}
                 disabled
               />
 
               {/* Only show the Feide ID field if the value is present */}
-              {guestData.feide_id && (
+              {initialGuestData.feide_id && (
                 <TextField
                   id="feide_id"
                   label={t('feideId')}
-                  value={guestData.feide_id}
+                  value={initialGuestData.feide_id}
                   disabled
                 />
               )}
@@ -387,7 +405,7 @@ const GuestRegisterStep = forwardRef(
                   )}
                 />
               </Box>
-              {guestData.authentication_method ===
+              {initialGuestData.authentication_method ===
                 AuthenticationMethod.Invite && (
                 <>
                   {/* The guest should fill in one of national ID number or passport number */}
@@ -488,7 +506,7 @@ const GuestRegisterStep = forwardRef(
                 </>
               )}
 
-              {guestData.authentication_method ===
+              {initialGuestData.authentication_method ===
                 AuthenticationMethod.Feide && (
                 <TextField
                   id="national_id_number"
@@ -508,8 +526,8 @@ const GuestRegisterStep = forwardRef(
                 id="ou-unit"
                 value={
                   i18n.language === 'en'
-                    ? guestData.ou_name_en
-                    : guestData.ou_name_nb
+                    ? initialGuestData.ou_name_en
+                    : initialGuestData.ou_name_nb
                 }
                 label={t('ou')}
                 disabled
@@ -520,8 +538,8 @@ const GuestRegisterStep = forwardRef(
                 label={t('input.roleType')}
                 value={
                   i18n.language === 'en'
-                    ? guestData.role_name_en
-                    : guestData.role_name_nb
+                    ? initialGuestData.role_name_en
+                    : initialGuestData.role_name_nb
                 }
                 disabled
               />
@@ -529,7 +547,7 @@ const GuestRegisterStep = forwardRef(
               <TextField
                 id="rolePeriod"
                 label={t('period')}
-                value={`${guestData.role_start} - ${guestData.role_end}`}
+                value={`${initialGuestData.role_start} - ${initialGuestData.role_end}`}
                 disabled
               />
 
@@ -538,7 +556,7 @@ const GuestRegisterStep = forwardRef(
                 label={t('input.comment')}
                 multiline
                 rows={5}
-                value={guestData.comment}
+                value={initialGuestData.comment}
                 disabled
               />
             </Stack>
diff --git a/frontend/src/routes/guest/register/submitSuccessPage.tsx b/frontend/src/routes/guest/register/steps/success.tsx
similarity index 90%
rename from frontend/src/routes/guest/register/submitSuccessPage.tsx
rename to frontend/src/routes/guest/register/steps/success.tsx
index 1c7e394612f1cb31ea7916795b21afbf23ba8e73..7ac97a900dfc7d178ecfd6db499e9b7b92da7161 100644
--- a/frontend/src/routes/guest/register/submitSuccessPage.tsx
+++ b/frontend/src/routes/guest/register/steps/success.tsx
@@ -1,11 +1,10 @@
 import React from 'react'
 import { useTranslation } from 'react-i18next'
+import { useHistory } from 'react-router-dom'
 
 import { Box, Button } from '@mui/material'
 
-import { useHistory } from 'react-router-dom'
-
-const StepSubmitSuccessGuest = () => {
+const GuestSuccessStep = () => {
   const { t } = useTranslation(['common'])
   const history = useHistory()
 
@@ -38,4 +37,4 @@ const StepSubmitSuccessGuest = () => {
   )
 }
 
-export default StepSubmitSuccessGuest
+export default GuestSuccessStep
diff --git a/frontend/src/routes/invitelink/index.tsx b/frontend/src/routes/invitelink/index.tsx
index cfaa21ed82dff761a07eb20afbd40b852e8ee854..fd8d63f056582d59de1a1774688a6d13506e8ec2 100644
--- a/frontend/src/routes/invitelink/index.tsx
+++ b/frontend/src/routes/invitelink/index.tsx
@@ -11,7 +11,7 @@ function InviteLink() {
   useEffect(() => {
     fetch('/api/ui/v1/invitecheck/', submitJsonOpts('POST', { uuid: id }))
   }, [])
-  setCookie('redirect', 'guestregister')
+  setCookie('redirect', '/guestregister')
   return <Redirect to="/invite" />
 }
 
diff --git a/frontend/src/routes/sponsor/register/frontPage.tsx b/frontend/src/routes/sponsor/register/frontPage.tsx
index 21b38e57ec36fcda71249d411f5c1bcf10a20668..4f0fbb012e88bd95d0c2aea2115d3772f04c4366 100644
--- a/frontend/src/routes/sponsor/register/frontPage.tsx
+++ b/frontend/src/routes/sponsor/register/frontPage.tsx
@@ -70,7 +70,7 @@ function FrontPage() {
         variant="contained"
         color="secondary"
         component={Link}
-        to="register/new"
+        to="/register/new"
       >
         {t('register.registerButtonText')}
       </Button>
diff --git a/frontend/src/utils/index.ts b/frontend/src/utils/index.ts
index 88b23193afdf34bca4ddb5f26a20dd3227d518f1..2e993f03bff6a5cfd9a328ecae548157973b36e4 100644
--- a/frontend/src/utils/index.ts
+++ b/frontend/src/utils/index.ts
@@ -55,6 +55,15 @@ export function submitJsonOpts(method: string, data: object): RequestInit {
   }
 }
 
+export function fetchJsonOpts(): RequestInit {
+  return {
+    headers: {
+      'Content-Type': 'application/json',
+    },
+    credentials: 'same-origin',
+  }
+}
+
 export function isValidFnr(
   data: string | undefined,
   allowEmpty = false
@@ -110,6 +119,15 @@ export function splitPhoneNumber(phoneNumber: string): [string, string] {
   ]
 }
 
+export function getLocalized(
+  obj: any,
+  prefix: string,
+  locale: string
+): string | null {
+  const key = prefix + locale
+  return Object.prototype.hasOwnProperty.call(obj, key) ? obj[key] : null
+}
+
 export function parseRole(role: FetchedRole): Role {
   return {
     id: role.id,
diff --git a/greg/admin.py b/greg/admin.py
index c19389dce61a8aa022051be0338ce6a6f0e0d6ca..c850afc5309afa8dbd95e8acedeafa01a265b6e4 100644
--- a/greg/admin.py
+++ b/greg/admin.py
@@ -1,4 +1,5 @@
 from django.contrib import admin
+from django.contrib.admin import display
 from reversion.admin import VersionAdmin
 
 from greg.models import (
@@ -10,8 +11,9 @@ from greg.models import (
     Role,
     RoleType,
     Identity,
-    ConsentType,
     Consent,
+    ConsentChoice,
+    ConsentType,
     OrganizationalUnit,
     Sponsor,
     SponsorOrganizationalUnit,
@@ -35,6 +37,11 @@ class ConsentInline(admin.TabularInline):
     extra = 1
 
 
+class ConsentChoiceInline(admin.TabularInline):
+    model = ConsentChoice
+    extra = 1
+
+
 class PersonAdmin(VersionAdmin):
     list_display = (
         "first_name",
@@ -52,11 +59,23 @@ class PersonAdmin(VersionAdmin):
 
 
 class RoleAdmin(VersionAdmin):
-    list_display = ("id", "person", "type")
+    list_display = (
+        "id",
+        "person",
+        "type",
+        "start_date",
+        "end_date",
+        "orgunit",
+        "sponsor",
+    )
     search_fields = (
         "person__id",
+        "person__first_name",
+        "person__last_name",
         "type__id",
+        "type__identifier",
     )
+    list_filter = ("type",)
     raw_id_fields = ("person", "type")
     readonly_fields = ("id", "created", "updated")
 
@@ -76,11 +95,10 @@ class ConsentAdmin(VersionAdmin):
     list_display = ("id", "person", "get_consent_type_name")
     readonly_fields = ("id", "created", "updated")
 
+    @display(description="Consent name")
     def get_consent_type_name(self, obj):
         return obj.type.name_en
 
-    get_consent_type_name.short_description = "Consent name"  # type: ignore
-
 
 class ConsentTypeAdmin(VersionAdmin):
     list_display = (
@@ -91,6 +109,7 @@ class ConsentTypeAdmin(VersionAdmin):
         "mandatory",
     )
     readonly_fields = ("id", "created", "updated")
+    inlines = (ConsentChoiceInline,)
 
 
 class OuIdentifierInline(admin.TabularInline):
@@ -126,7 +145,19 @@ class SponsorOrganizationalUnitAdmin(VersionAdmin):
 
 
 class InvitationAdmin(VersionAdmin):
-    list_display = ("id",)
+    list_display = ("id", "role", "get_role_person", "get_role_type", "get_role_ou")
+
+    @display(description="Person")
+    def get_role_person(self, obj):
+        return obj.role.person
+
+    @display(description="Role type")
+    def get_role_type(self, obj):
+        return obj.role.type
+
+    @display(description="OU")
+    def get_role_ou(self, obj):
+        return obj.role.orgunit
 
 
 class InvitationLinkAdmin(VersionAdmin):
diff --git a/greg/api/serializers/consent_type.py b/greg/api/serializers/consent_type.py
index b7a5da36b436c671175dd9b636a6e49da86a46b0..407777f6026e7ae0602988889657b2e764d2b71c 100644
--- a/greg/api/serializers/consent_type.py
+++ b/greg/api/serializers/consent_type.py
@@ -1,12 +1,33 @@
 from rest_framework.serializers import ModelSerializer
 
-from greg.models import ConsentType
+from greg.models import ConsentChoice, ConsentType
+
+
+class ConsentChoiceSerializer(ModelSerializer):
+    class Meta:
+        model = ConsentChoice
+        fields = ["value", "text_en", "text_nb", "text_nn"]
 
 
 class ConsentTypeSerializer(ModelSerializer):
+    choices = ConsentChoiceSerializer(many=True)
+
     class Meta:
         model = ConsentType
-        fields = "__all__"
+        fields = (
+            "id",
+            "identifier",
+            "name_en",
+            "name_nb",
+            "name_nn",
+            "description_en",
+            "description_nb",
+            "description_nn",
+            "mandatory",
+            "user_allowed_to_change",
+            "valid_from",
+            "choices",
+        )
 
 
 class ConsentTypeSerializerBrief(ModelSerializer):
diff --git a/greg/migrations/0016_consent_choices.py b/greg/migrations/0016_consent_choices.py
new file mode 100644
index 0000000000000000000000000000000000000000..de53dbe7facbd007455e2e54083f210410dff115
--- /dev/null
+++ b/greg/migrations/0016_consent_choices.py
@@ -0,0 +1,58 @@
+# Generated by Django 3.2.9 on 2021-11-23 08:09
+
+import dirtyfields.dirtyfields
+from django.db import migrations, models
+import django.db.models.deletion
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('greg', '0015_add_feide_email'),
+    ]
+
+    operations = [
+        migrations.RemoveField(
+            model_name='consenttype',
+            name='link_en',
+        ),
+        migrations.RemoveField(
+            model_name='consenttype',
+            name='link_nb',
+        ),
+        migrations.AddField(
+            model_name='consenttype',
+            name='description_nn',
+            field=models.TextField(default=''),
+            preserve_default=False,
+        ),
+        migrations.AddField(
+            model_name='consenttype',
+            name='name_nn',
+            field=models.CharField(default='', max_length=256),
+            preserve_default=False,
+        ),
+        migrations.CreateModel(
+            name='ConsentChoice',
+            fields=[
+                ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
+                ('created', models.DateTimeField(auto_now_add=True)),
+                ('updated', models.DateTimeField(auto_now=True)),
+                ('value', models.CharField(max_length=128)),
+                ('text_en', models.CharField(max_length=512)),
+                ('text_nb', models.CharField(max_length=512)),
+                ('text_nn', models.CharField(max_length=512)),
+                ('consent_type', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, related_name='choices', to='greg.consenttype')),
+            ],
+            bases=(dirtyfields.dirtyfields.DirtyFieldsMixin, models.Model),
+        ),
+        migrations.AddField(
+            model_name='consent',
+            name='choice',
+            field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.PROTECT, related_name='consents', to='greg.consentchoice'),
+        ),
+        migrations.AddConstraint(
+            model_name='consentchoice',
+            constraint=models.UniqueConstraint(fields=('consent_type', 'value'), name='consent_choice_type_value_unique'),
+        ),
+    ]
diff --git a/greg/models.py b/greg/models.py
index 83b64287984912fe3d870e05b5178c15b08e3815..6591172b3150e20b38823ddcdfa7349d8ce204cd 100644
--- a/greg/models.py
+++ b/greg/models.py
@@ -329,10 +329,10 @@ class ConsentType(BaseModel):
     identifier = models.SlugField(max_length=64, unique=True)
     name_en = models.CharField(max_length=256)
     name_nb = models.CharField(max_length=256)
+    name_nn = models.CharField(max_length=256)
     description_en = models.TextField()
     description_nb = models.TextField()
-    link_en = models.URLField(null=True)
-    link_nb = models.URLField(null=True)
+    description_nn = models.TextField()
     valid_from = models.DateField(default=date.today)
     user_allowed_to_change = models.BooleanField()
     mandatory = models.BooleanField(default=False)
@@ -351,6 +351,42 @@ class ConsentType(BaseModel):
         )
 
 
+class ConsentChoice(BaseModel):
+    """
+    Describes an option associated with a consent type.
+    """
+
+    consent_type = models.ForeignKey(
+        "ConsentType", on_delete=models.PROTECT, related_name="choices"
+    )
+    value = models.CharField(max_length=128)
+    text_en = models.CharField(max_length=512)
+    text_nb = models.CharField(max_length=512)
+    text_nn = models.CharField(max_length=512)
+
+    class Meta:
+        constraints = (
+            models.UniqueConstraint(
+                fields=["consent_type", "value"],
+                name="consent_choice_type_value_unique",
+            ),
+        )
+
+    def __str__(self):
+        return "{} ({})".format(str(self.text_en or self.text_nb), self.value)
+
+    def __repr__(self):
+        return "{}(id={!r}, consent_type={!r} value={!r}, text_en={!r}, text_nb={!r}, text_nn={!r})".format(
+            self.__class__.__name__,
+            self.pk,
+            self.consent_type,
+            self.value,
+            self.text_en,
+            self.text_nb,
+            self.text_nn,
+        )
+
+
 class Consent(BaseModel):
     """
     Links a person and a consent he has given.
@@ -362,7 +398,12 @@ class Consent(BaseModel):
     type = models.ForeignKey(
         "ConsentType", on_delete=models.PROTECT, related_name="persons"
     )
-    # If the date is blank it means the person has not given consent yet
+    choice = models.ForeignKey(
+        "ConsentChoice",
+        on_delete=models.PROTECT,
+        related_name="consents",
+        null=True,
+    )
     consent_given_at = models.DateField(null=True)
 
     class Meta:
diff --git a/greg/tests/api/test_consent_type.py b/greg/tests/api/test_consent_type.py
index 42cdb8d4894a3f1676d608895bc6186bde68742c..7851f858c95a295d9aa1d035071746fc9472351f 100644
--- a/greg/tests/api/test_consent_type.py
+++ b/greg/tests/api/test_consent_type.py
@@ -12,6 +12,30 @@ def test_get_consent_type(client, consent_type_foo: ConsentType):
     )
     assert resp.status_code == status.HTTP_200_OK
     data = resp.json()
-    assert data.get("id") == consent_type_foo.id
-    assert data.get("identifier") == consent_type_foo.identifier
-    assert data.get("name_en") == consent_type_foo.name_en
+    assert data == {
+        "id": consent_type_foo.id,
+        "identifier": consent_type_foo.identifier,
+        "name_en": consent_type_foo.name_en,
+        "name_nb": consent_type_foo.name_nb,
+        "name_nn": consent_type_foo.name_nn,
+        "description_en": consent_type_foo.description_en,
+        "description_nb": consent_type_foo.description_nb,
+        "description_nn": consent_type_foo.description_nn,
+        "mandatory": False,
+        "user_allowed_to_change": True,
+        "valid_from": "2018-01-20",
+        "choices": [
+            {
+                "value": "yes",
+                "text_en": "Yes",
+                "text_nb": "Ja",
+                "text_nn": "Ja",
+            },
+            {
+                "value": "no",
+                "text_en": "No",
+                "text_nb": "Nei",
+                "text_nn": "Nei",
+            },
+        ],
+    }
diff --git a/greg/tests/conftest.py b/greg/tests/conftest.py
index 04dfe76cb87facce8fa4db2e09cd8b09c772c34d..8315054552d2a98a70dd77e34af6991e5b80d5e8 100644
--- a/greg/tests/conftest.py
+++ b/greg/tests/conftest.py
@@ -9,6 +9,7 @@ import pytest
 
 from greg.models import (
     Consent,
+    ConsentChoice,
     Notification,
     OuIdentifier,
     Person,
@@ -167,21 +168,6 @@ def role_person_foo(
     return Role.objects.get(id=role.id)
 
 
-@pytest.fixture
-def consent_type() -> ConsentType:
-    ct = ConsentType.objects.create(
-        identifier="it-guidelines",
-        name_en="IT Guidelines",
-        name_nb="IT Regelverk",
-        description_en="IT Guidelines description",
-        description_nb="IT Regelverk beskrivelse",
-        link_en="https://example.org/it-guidelines",
-        link_nb="https://example.org/it-guidelines",
-        user_allowed_to_change=False,
-    )
-    return ConsentType.objects.get(id=ct.id)
-
-
 @pytest.fixture
 def role_type_foo() -> RoleType:
     rt = RoleType.objects.create(identifier="role_foo", name_en="Role Foo")
@@ -195,27 +181,43 @@ def role_type_bar() -> RoleType:
 
 
 @pytest.fixture
-def consent_fixture(person, consent_type):
+def consent_fixture(person, consent_type_foo):
     consent_given_date = "2021-06-20"
     Consent.objects.create(
-        person=person, type=consent_type, consent_given_at=consent_given_date
+        person=person, type=consent_type_foo, consent_given_at=consent_given_date
     )
     return Consent.objects.get(id=1)
 
 
 @pytest.fixture
 def consent_type_foo() -> ConsentType:
-    return ConsentType.objects.create(
-        identifier="test_consent",
-        name_en="Test1",
-        name_nb="Test2",
-        description_en="Test description",
-        description_nb="Test beskrivelse",
-        link_en="https://example.org",
-        link_nb="https://example.org",
+    consent_foo = ConsentType.objects.create(
+        identifier="foo",
+        name_en="Foo",
+        name_nb="Fu",
+        name_nn="F",
+        description_en="Description",
+        description_nb="Beskrivelse",
+        description_nn="Beskriving",
         valid_from="2018-01-20",
         user_allowed_to_change=True,
+        mandatory=False,
+    )
+    ConsentChoice.objects.create(
+        consent_type=consent_foo,
+        value="yes",
+        text_en="Yes",
+        text_nb="Ja",
+        text_nn="Ja",
+    )
+    ConsentChoice.objects.create(
+        consent_type=consent_foo,
+        value="no",
+        text_en="No",
+        text_nb="Nei",
+        text_nn="Nei",
     )
+    return ConsentType.objects.get(id=consent_foo.id)
 
 
 @pytest.fixture
diff --git a/greg/tests/models/test_consent.py b/greg/tests/models/test_consent.py
index 9efa4b68ddf6c04c19fbe74c1164b66b46d7e293..94745e2b9d6083c645ec6b4b7052b7302c7ead20 100644
--- a/greg/tests/models/test_consent.py
+++ b/greg/tests/models/test_consent.py
@@ -10,35 +10,34 @@ from greg.models import (
 
 
 @pytest.mark.django_db
-def test_add_consent_to_person(person: Person, consent_type: ConsentType):
+def test_add_consent_to_person(person: Person, consent_type_foo: ConsentType):
     consent_given_date = "2021-06-20"
     Consent.objects.create(
-        person=person, type=consent_type, consent_given_at=consent_given_date
+        person=person, type=consent_type_foo, consent_given_at=consent_given_date
     )
     consents = Consent.objects.filter(person_id=person.id)
 
     assert len(consents) == 1
     assert consents[0].person_id == person.id
-    assert consents[0].type_id == consent_type.id
+    assert consents[0].type_id == consent_type_foo.id
     assert consents[0].consent_given_at == datetime.date(2021, 6, 20)
 
 
 @pytest.mark.django_db
 def test_add_not_acknowledged_consent_to_person(
-    person: Person, consent_type: ConsentType
+    person: Person, consent_type_foo: ConsentType
 ):
-    Consent.objects.create(person=person, type=consent_type)
+    Consent.objects.create(person=person, type=consent_type_foo)
     consents = Consent.objects.filter(person_id=person.id)
     assert len(consents) == 1
     assert consents[0].person_id == person.id
-    assert consents[0].type_id == consent_type.id
+    assert consents[0].type_id == consent_type_foo.id
     assert consents[0].consent_given_at is None
 
 
 @pytest.mark.django_db
 def test_consent_repr(consent_fixture):
-    today = datetime.date.today()
     assert (
         repr(consent_fixture)
-        == f"Consent(id=1, person=Person(id=1, first_name='Test', last_name='Tester'), type=ConsentType(id=1, identifier='it-guidelines', name_en='IT Guidelines', valid_from={today!r}, user_allowed_to_change=False), consent_given_at=datetime.date(2021, 6, 20))"
+        == "Consent(id=1, person=Person(id=1, first_name='Test', last_name='Tester'), type=ConsentType(id=1, identifier='foo', name_en='Foo', valid_from=datetime.date(2018, 1, 20), user_allowed_to_change=True), consent_given_at=datetime.date(2021, 6, 20))"
     )
diff --git a/greg/tests/models/test_consent_type.py b/greg/tests/models/test_consent_type.py
new file mode 100644
index 0000000000000000000000000000000000000000..5edfcda3cb3112d7c6bf2faf468f9ed1da984b2b
--- /dev/null
+++ b/greg/tests/models/test_consent_type.py
@@ -0,0 +1,14 @@
+import pytest
+
+
+@pytest.mark.django_db
+def test_consent_type_repr(consent_type_foo):
+    assert (
+        repr(consent_type_foo)
+        == "ConsentType(id=1, identifier='foo', name_en='Foo', valid_from=datetime.date(2018, 1, 20), user_allowed_to_change=True)"
+    )
+
+
+@pytest.mark.django_db
+def test_consent_type_str(consent_type_foo):
+    assert str(consent_type_foo) == "Foo (foo)"
diff --git a/greg/tests/models/test_consenttype.py b/greg/tests/models/test_consenttype.py
deleted file mode 100644
index d69e340b417f95c560a9cd907c1d28f3fec47b1a..0000000000000000000000000000000000000000
--- a/greg/tests/models/test_consenttype.py
+++ /dev/null
@@ -1,17 +0,0 @@
-from datetime import date
-
-import pytest
-
-
-@pytest.mark.django_db
-def test_consenttype_repr(consent_type):
-    today = date.today()
-    assert (
-        repr(consent_type)
-        == f"ConsentType(id=1, identifier='it-guidelines', name_en='IT Guidelines', valid_from={today!r}, user_allowed_to_change=False)"
-    )
-
-
-@pytest.mark.django_db
-def test_consenttype_str(consent_type):
-    assert str(consent_type) == "IT Guidelines (it-guidelines)"
diff --git a/greg/tests/populate_database.py b/greg/tests/populate_database.py
index f4e103f1d569a766b3fcc2fb237cdb32bd289716..3ac7e25cbf74dff9a2099c67188f23b057eaf346 100644
--- a/greg/tests/populate_database.py
+++ b/greg/tests/populate_database.py
@@ -100,10 +100,10 @@ class DatabasePopulation:
                     identifier=self.faker.slug(),
                     name_en=self.faker.sentence(nb_words=6),
                     name_nb=self.faker.sentence(nb_words=6),
+                    name_nn=self.faker.sentence(nb_words=6),
                     description_en=self.faker.paragraph(nb_sentences=5),
                     description_nb=self.faker.paragraph(nb_sentences=5),
-                    link_en=self.faker.url(),
-                    link_nb=self.faker.url(),
+                    description_nn=self.faker.paragraph(nb_sentences=5),
                     user_allowed_to_change=self.random.random() > 0.5,
                 )
             )
diff --git a/greg/tests/populate_fixtures.py b/greg/tests/populate_fixtures.py
index 881253178e8c9454dd792088c70c031e615370e5..754c59f58d0d4a19233b6694c58de33b0e2bba91 100644
--- a/greg/tests/populate_fixtures.py
+++ b/greg/tests/populate_fixtures.py
@@ -30,6 +30,7 @@ from django.utils import timezone
 
 from greg.models import (
     Consent,
+    ConsentChoice,
     ConsentType,
     Identity,
     Invitation,
@@ -69,6 +70,7 @@ class DatabasePopulation:
         with connection.cursor() as cursor:
             for table in (
                 "greg_consent",
+                "greg_consentchoice",
                 "greg_consenttype",
                 "greg_notification",
                 "greg_identity",
@@ -79,28 +81,54 @@ class DatabasePopulation:
                 "greg_roletype",
                 "greg_ouidentifier",
                 "greg_organizationalunit",
-                "greg_person",
                 "gregui_greguserprofile",
+                "greg_person",
                 "greg_sponsor",
             ):
                 logging.info("purging table %s", table)
                 cursor.execute(f"DELETE FROM {table}")
         logger.info("...tables purged")
 
-    def _add_consenttypes(self):
-        ConsentType.objects.create(
+    def _add_consent_types_and_choices(self):
+        mandatory = ConsentType.objects.create(
             identifier=CONSENT_IDENT_MANDATORY,
-            name_en="Mandatory consent type",
-            name_nb="PÃ¥krevd samtykketype",
+            name_en="Mandatory consent",
+            name_nb="Obligatorisk samtykke",
+            name_nn="Obligatorisk samtykke",
+            description_en="Description for mandatory consent",
+            description_nb="Beskrivelse for obligatorisk samtykke",
+            description_nn="Forklaring for obligatorisk samtykke",
             user_allowed_to_change=False,
             mandatory=True,
         )
-        ConsentType.objects.create(
+        ConsentChoice.objects.create(
+            consent_type=mandatory,
+            value="yes",
+            text_en="Yes",
+            text_nb="Ja",
+            text_nn="Ja",
+        )
+        optional = ConsentType.objects.create(
             identifier=CONSENT_IDENT_OPTIONAL,
-            name_en="Optional consent type",
-            name_nb="Valgfri samtykketype",
+            name_en="Optional consent",
+            name_nb="Valgfritt samtykke",
+            name_nn="Valfritt samtykke",
             user_allowed_to_change=False,
         )
+        ConsentChoice.objects.create(
+            consent_type=optional,
+            value="yes",
+            text_en="Yes",
+            text_nb="Ja",
+            text_nn="Ja",
+        )
+        ConsentChoice.objects.create(
+            consent_type=optional,
+            value="no",
+            text_en="No",
+            text_nb="Nei",
+            text_nn="Nei",
+        )
 
     def _add_ous_with_identifiers(self):
         """
@@ -341,7 +369,7 @@ class DatabasePopulation:
     def populate_database(self):
         logger.info("populating db...")
         # Add the types, sponsors and ous
-        self._add_consenttypes()
+        self._add_consent_types_and_choices()
         self._add_ous_with_identifiers()
         self._add_roletypes()
         self._add_sponsors()
diff --git a/greg/tests/test_notifications.py b/greg/tests/test_notifications.py
index c0fd917da896d0866d72b3dc0729bc357c1a5014..3e5d4ac29861647d81b2e8e6bf66cf50ec7313e9 100644
--- a/greg/tests/test_notifications.py
+++ b/greg/tests/test_notifications.py
@@ -145,9 +145,9 @@ def test_role_delete_notification(
 
 
 @pytest.mark.django_db
-def test_consent_add_notification(person: Person, consent_type: ConsentType):
+def test_consent_add_notification(person: Person, consent_type_foo: ConsentType):
     consent = Consent.objects.create(
-        person=person, type=consent_type, consent_given_at="2021-06-20"
+        person=person, type=consent_type_foo, consent_given_at="2021-06-20"
     )
     notifications = Notification.objects.filter(object_type="Consent")
     assert len(notifications) == 1
@@ -160,11 +160,11 @@ def test_consent_add_notification(person: Person, consent_type: ConsentType):
 
 
 @pytest.mark.django_db
-def test_consent_update_notification(person: Person, consent_type: ConsentType):
+def test_consent_update_notification(person: Person, consent_type_foo: ConsentType):
     consent = Consent.objects.create(
-        person=person, type=consent_type, consent_given_at="2021-06-20"
+        person=person, type=consent_type_foo, consent_given_at="2021-06-20"
     )
-    consents = Consent.objects.filter(person=person, type=consent_type)
+    consents = Consent.objects.filter(person=person, type=consent_type_foo)
     consents[0].consent_given_at = "2021-06-21"
     consents[0].save()
 
@@ -179,11 +179,11 @@ def test_consent_update_notification(person: Person, consent_type: ConsentType):
 
 
 @pytest.mark.django_db
-def test_consent_delete_notification(person: Person, consent_type: ConsentType):
+def test_consent_delete_notification(person: Person, consent_type_foo: ConsentType):
     consent = Consent.objects.create(
-        person=person, type=consent_type, consent_given_at="2021-06-20"
+        person=person, type=consent_type_foo, consent_given_at="2021-06-20"
     )
-    consents = Consent.objects.filter(person=person, type=consent_type)
+    consents = Consent.objects.filter(person=person, type=consent_type_foo)
     consents[0].delete()
     notifications = Notification.objects.filter(object_type="Consent")
 
@@ -193,8 +193,8 @@ def test_consent_delete_notification(person: Person, consent_type: ConsentType):
     meta = notifications[0].meta
     assert meta["person_id"] == person.id
     assert meta["consent_id"] == consent.id
-    assert meta["consent_type"] == consent_type.identifier
-    assert meta["consent_type_id"] == consent_type.id
+    assert meta["consent_type"] == consent_type_foo.identifier
+    assert meta["consent_type_id"] == consent_type_foo.id
 
 
 @pytest.mark.django_db
diff --git a/gregui/api/serializers/guest.py b/gregui/api/serializers/guest.py
index 112b200fed9915223488070fa19723484387c3e8..471f459de8c20b07ed0860f9ee088ecb5ed2b985 100644
--- a/gregui/api/serializers/guest.py
+++ b/gregui/api/serializers/guest.py
@@ -1,13 +1,29 @@
 import datetime
 
+from django.utils.timezone import now
 from rest_framework import serializers
+from rest_framework.exceptions import ValidationError
 
-from greg.models import Identity, Person
+from greg.models import Consent, ConsentChoice, ConsentType, Identity, Person
 from gregui.validation import (
     validate_phone_number,
     validate_norwegian_national_id_number,
 )
 
+# pylint: disable=W0223
+class GuestConsentChoiceSerializer(serializers.Serializer):
+    type = serializers.CharField(required=True)
+    choice = serializers.CharField(required=True)
+
+    def validate(self, attrs):
+        """Check that the combination of consent type and choice exists."""
+        choice = ConsentChoice.objects.filter(
+            consent_type__identifier=attrs["type"], value=attrs["choice"]
+        )
+        if not choice.exists():
+            raise serializers.ValidationError("invalid consent type or choice")
+        return attrs
+
 
 class GuestRegisterSerializer(serializers.ModelSerializer):
     first_name = serializers.CharField(required=False, min_length=1)
@@ -23,6 +39,7 @@ class GuestRegisterSerializer(serializers.ModelSerializer):
     )
     passport = serializers.CharField(required=False)
     date_of_birth = serializers.DateField(required=False)
+    consents = GuestConsentChoiceSerializer(required=False, many=True, write_only=True)
 
     def update(self, instance, validated_data):
         if "email" in validated_data:
@@ -59,8 +76,35 @@ class GuestRegisterSerializer(serializers.ModelSerializer):
         if "date_of_birth" in validated_data:
             instance.date_of_birth = validated_data["date_of_birth"]
 
+        consents = validated_data.get("consents", {})
+        self._handle_consents(person=instance, consents=consents)
+
         return instance
 
+    def _handle_consents(self, person, consents):
+        consent_types = [x["type"] for x in consents]
+        mandatory_consents = ConsentType.objects.filter(mandatory=True).values_list(
+            "identifier", flat=True
+        )
+        missing_consents = list(set(mandatory_consents) - set(consent_types))
+        if missing_consents:
+            raise ValidationError(f"missing mandatory consents {missing_consents}")
+
+        for consent in consents:
+            consent_type = ConsentType.objects.get(identifier=consent["type"])
+            choice = ConsentChoice.objects.get(
+                consent_type=consent_type, value=consent["choice"]
+            )
+            consent_instance, created = Consent.objects.get_or_create(
+                type=consent_type,
+                person=person,
+                choice=choice,
+                defaults={"consent_given_at": now()},
+            )
+            if not created and consent_instance.choice != choice:
+                consent_instance.choice = choice
+                consent_instance.save()
+
     def validate_date_of_birth(self, date_of_birth):
         today = datetime.date.today()
 
@@ -85,6 +129,7 @@ class GuestRegisterSerializer(serializers.ModelSerializer):
             "fnr",
             "passport",
             "date_of_birth",
+            "consents",
         )
         read_only_fields = ("id",)
 
diff --git a/gregui/api/urls.py b/gregui/api/urls.py
index d7fa5f1febaa1b411ae31c6ed5e84fbe4dd587c6..3b991355256b6eb6fb093284013e50408fa90aca 100644
--- a/gregui/api/urls.py
+++ b/gregui/api/urls.py
@@ -11,6 +11,7 @@ from gregui.api.views.invitation import (
 from gregui.api.views.ou import OusViewSet
 from gregui.api.views.person import GuestInfoViewSet, PersonSearchViewSet, PersonViewSet
 from gregui.api.views.role import RoleInfoViewSet
+from gregui.api.views.consent import ConsentTypeViewSet
 from gregui.api.views.roletypes import RoleTypeViewSet
 from gregui.api.views.unit import UnitsViewSet
 
@@ -23,6 +24,7 @@ router.register(r"guests/", GuestInfoViewSet, basename="guests")
 
 urlpatterns = router.urls
 urlpatterns += [
+    re_path(r"consenttypes/$", ConsentTypeViewSet.as_view(), name="consent-types"),
     re_path(r"roletypes/$", RoleTypeViewSet.as_view(), name="role-types"),
     re_path(r"units/$", UnitsViewSet.as_view(), name="units"),
     path("invited/", InvitedGuestView.as_view(), name="invited-info"),
diff --git a/gregui/api/views/consent.py b/gregui/api/views/consent.py
new file mode 100644
index 0000000000000000000000000000000000000000..f9f549c47bdb9e59b5bcd001943847c66ed867d5
--- /dev/null
+++ b/gregui/api/views/consent.py
@@ -0,0 +1,11 @@
+from rest_framework import permissions
+from rest_framework.generics import ListAPIView
+
+from greg.models import ConsentType
+from greg.api.serializers.consent_type import ConsentTypeSerializer
+
+
+class ConsentTypeViewSet(ListAPIView):
+    queryset = ConsentType.objects.all().order_by("id")
+    permission_classes = [permissions.AllowAny]
+    serializer_class = ConsentTypeSerializer
diff --git a/gregui/api/views/invitation.py b/gregui/api/views/invitation.py
index 88531650500f689621c08b79a0e9214aeaa3a7e3..af633e83e82470f5e0faffd8dda283319fec4a22 100644
--- a/gregui/api/views/invitation.py
+++ b/gregui/api/views/invitation.py
@@ -1,6 +1,6 @@
 import logging
 from enum import Enum
-from typing import Optional
+from typing import Optional, List
 
 from django.core import exceptions
 from django.db import transaction
@@ -154,8 +154,9 @@ class InvitedGuestView(GenericAPIView):
         "mobile_phone",
         "passport",
         "date_of_birth",
+        "consents",
     ]
-    fields_allowed_to_update_if_feide = ["mobile_phone"]
+    fields_allowed_to_update_if_feide = ["mobile_phone", "consents"]
 
     def get(self, request, *args, **kwargs):
         """
@@ -238,13 +239,18 @@ class InvitedGuestView(GenericAPIView):
 
         # If there is a Feide ID registered with the guest, assume that the name is also coming from there
         feide_id = self._get_identity_or_none(person, Identity.IdentityType.FEIDE_ID)
-        if not self._only_allowed_fields_in_request(
+
+        illegal_fields = self._illegal_fields(
             data,
             self.fields_allowed_to_update_if_invite
             if feide_id is None
             else self.fields_allowed_to_update_if_feide,
-        ):
-            return Response(status=status.HTTP_400_BAD_REQUEST)
+        )
+        if illegal_fields:
+            return Response(
+                status=status.HTTP_400_BAD_REQUEST,
+                data={"error": {"cannot_update_fields": illegal_fields}},
+            )
 
         if self._verified_fnr_already_exists(person) and fnr:
             # The user should not be allowed to change a verified fnr
@@ -281,14 +287,11 @@ class InvitedGuestView(GenericAPIView):
             return False
 
     @staticmethod
-    def _only_allowed_fields_in_request(request_data, fields_allowed_to_update) -> bool:
+    def _illegal_fields(request_data, changeable_fields) -> List[str]:
         # Check how many of the allowed fields are filled in
         person_data = request_data["person"]
-        number_of_fields_filled_in = sum(
-            map(lambda x: x in person_data.keys(), fields_allowed_to_update)
-        )
-        # Check that there are no other fields filled in
-        return number_of_fields_filled_in == len(person_data.keys())
+        changed_fields = person_data.keys()
+        return list(set(changed_fields) - set(changeable_fields))
 
     @staticmethod
     def _get_identity_or_none(
diff --git a/gregui/tests/api/views/test_consent.py b/gregui/tests/api/views/test_consent.py
new file mode 100644
index 0000000000000000000000000000000000000000..6da9cee54f5a579c33496231234ef09b3bc45594
--- /dev/null
+++ b/gregui/tests/api/views/test_consent.py
@@ -0,0 +1,45 @@
+import pytest
+
+from rest_framework import status
+from rest_framework.reverse import reverse
+
+
+@pytest.mark.django_db
+def test_get_consents(client, consent_type_foo, consent_type_bar):
+    response = client.get(reverse("gregui-v1:consent-types"))
+    assert response.status_code == status.HTTP_200_OK
+    assert response.json() == [
+        {
+            "choices": [
+                {"text_en": "Yes", "text_nb": "Ja", "text_nn": "Ja", "value": "yes"},
+                {"text_en": "No", "text_nb": "Nei", "text_nn": "Nei", "value": "no"},
+            ],
+            "description_en": "Description",
+            "description_nb": "Beskrivelse",
+            "description_nn": "Beskriving",
+            "id": 1,
+            "identifier": "foo",
+            "mandatory": False,
+            "name_en": "Foo",
+            "name_nb": "Fu",
+            "name_nn": "F",
+            "user_allowed_to_change": True,
+            "valid_from": "2018-01-20",
+        },
+        {
+            "choices": [
+                {"text_en": "Yes", "text_nb": "Ja", "text_nn": "Ja", "value": "yes"}
+            ],
+            "description_en": "Description",
+            "description_nb": "Beskrivelse",
+            "description_nn": "Beskriving",
+            "id": 2,
+            "identifier": "bar",
+            "mandatory": True,
+            "name_en": "Bar",
+            "name_nb": "Ba",
+            "name_nn": "B",
+            "user_allowed_to_change": True,
+            "valid_from": "2018-01-20",
+        },
+    ]
diff --git a/gregui/tests/api/views/test_invitation.py b/gregui/tests/api/views/test_invitation.py
index bd1e7d172e1a487c3c2a8e22f45b0c88a53f6e93..5883fea7c4ef326cb93e586e7b14a369a0d02bda 100644
--- a/gregui/tests/api/views/test_invitation.py
+++ b/gregui/tests/api/views/test_invitation.py
@@ -6,7 +6,7 @@ from rest_framework import status
 from rest_framework.reverse import reverse
 from rest_framework.test import APIClient
 
-from greg.models import InvitationLink, Person, Identity
+from greg.models import Consent, InvitationLink, Person, Identity
 
 
 @pytest.mark.django_db
@@ -209,7 +209,7 @@ def test_post_invited_info_valid_national_id_number(client, invited_person):
 
     response = client.post(url, data, format="json")
 
-    assert response.status_code == status.HTTP_200_OK
+    assert response.status_code == status.HTTP_200_OK, response.data
     person.refresh_from_db()
     assert person.fnr.value == fnr
 
@@ -229,7 +229,7 @@ def test_email_update(client, invited_person):
 
     data = {"person": {"mobile_phone": "+4797543992", "email": "test2@example.com"}}
     response = client.post(url, data, format="json")
-    assert response.status_code == status.HTTP_200_OK
+    assert response.status_code == status.HTTP_200_OK, response.data
 
     person.private_email.refresh_from_db()
     assert person.private_email.value == "test2@example.com"
@@ -255,7 +255,7 @@ def test_register_passport(client, invited_person):
 
     response = client.post(url, data, format="json")
 
-    assert response.status_code == status.HTTP_200_OK
+    assert response.status_code == status.HTTP_200_OK, response.data
     person.refresh_from_db()
 
     registered_passport = Identity.objects.filter(
@@ -346,7 +346,7 @@ def test_name_update_allowed_if_feide_identity_is_not_present(
         data,
         format="json",
     )
-    assert response.status_code == status.HTTP_200_OK
+    assert response.status_code == status.HTTP_200_OK, response.data
 
     # Check that the name has been updated in the database
     assert Person.objects.count() == 1
@@ -371,6 +371,7 @@ def test_post_info_fail_fnr_already_verified(client, invited_person_verified_nin
 
     # Verify rejection
     assert response.status_code == status.HTTP_400_BAD_REQUEST
+
     # Verify fnr was not changed
     person.refresh_from_db()
     assert person.fnr.value != fnr
@@ -432,3 +433,38 @@ def test_invalid_date_of_birth_rejected(client, invited_person_verified_nin):
 
     person = Person.objects.get()
     assert person.date_of_birth is None
+    response = client.post(url, data, format="json")
+
+
+@pytest.mark.django_db
+def test_saves_consents(client, invited_person, consent_type_foo, consent_type_bar):
+    person, invitation_link = invited_person
+    fnr = "11120618212"
+
+    session = client.session
+    session["invite_id"] = str(invitation_link.uuid)
+    session.save()
+
+    consents = [
+        {"type": consent_type_foo.identifier, "choice": "no"},
+        {"type": consent_type_bar.identifier, "choice": "yes"},
+    ]
+
+    data = {"person": {"mobile_phone": "+4797543992", "fnr": fnr, "consents": consents}}
+    url = reverse("gregui-v1:invited-info")
+    response = client.post(url, data, format="json")
+
+    assert response.status_code == status.HTTP_200_OK, response.data
+    person.refresh_from_db()
+
+    assert Consent.objects.filter(
+        person=person,
+        type=consent_type_foo,
+        choice=consent_type_foo.choices.filter(value="no").first(),
+    ).exists()
+
+    assert Consent.objects.filter(
+        person=person,
+        type=consent_type_bar,
+        choice=consent_type_bar.choices.filter(value="yes").first(),
+    ).exists()
diff --git a/gregui/tests/conftest.py b/gregui/tests/conftest.py
index 797760461eb0c73c363961cff2ebecbc697d8801..f30b5e001cfd65a8abe341975578cc70a12c1ac1 100644
--- a/gregui/tests/conftest.py
+++ b/gregui/tests/conftest.py
@@ -13,6 +13,8 @@ from rest_framework.authtoken.admin import User
 from rest_framework.test import APIClient
 
 from greg.models import (
+    ConsentType,
+    ConsentChoice,
     Identity,
     Invitation,
     InvitationLink,
@@ -490,3 +492,58 @@ You have been registered as a guest at {{ institution }} by {{ sponsor }}.
 To complete the registration of your guest account, please follow this link: {{ registration_link }}""",
     )
     return EmailTemplate.objects.get(id=et.id)
+
+
+@pytest.fixture
+def consent_type_foo() -> ConsentType:
+    type_foo = ConsentType.objects.create(
+        identifier="foo",
+        name_en="Foo",
+        name_nb="Fu",
+        name_nn="F",
+        description_en="Description",
+        description_nb="Beskrivelse",
+        description_nn="Beskriving",
+        valid_from="2018-01-20",
+        user_allowed_to_change=True,
+        mandatory=False,
+    )
+    ConsentChoice.objects.create(
+        consent_type=type_foo,
+        value="yes",
+        text_en="Yes",
+        text_nb="Ja",
+        text_nn="Ja",
+    )
+    ConsentChoice.objects.create(
+        consent_type=type_foo,
+        value="no",
+        text_en="No",
+        text_nb="Nei",
+        text_nn="Nei",
+    )
+    return ConsentType.objects.get(id=type_foo.id)
+
+
+@pytest.fixture
+def consent_type_bar() -> ConsentType:
+    type_bar = ConsentType.objects.create(
+        identifier="bar",
+        name_en="Bar",
+        name_nb="Ba",
+        name_nn="B",
+        description_en="Description",
+        description_nb="Beskrivelse",
+        description_nn="Beskriving",
+        valid_from="2018-01-20",
+        user_allowed_to_change=True,
+        mandatory=True,
+    )
+    ConsentChoice.objects.create(
+        consent_type=type_bar,
+        value="yes",
+        text_en="Yes",
+        text_nb="Ja",
+        text_nn="Ja",
+    )
+    return ConsentType.objects.get(id=type_bar.id)