> ## Documentation Index
> Fetch the complete documentation index at: https://docs.auth.energy/llms.txt
> Use this file to discover all available pages before exploring further.

# Create an Identity Record

> Creates an Identity Record holding the person-property relationship for a
consent or access registration. Returns an `ir` key that must be supplied
as `record-metadata.identity-record-ref` when creating the linked AccessRecord.

The Identity Record holds:
- `pii-principal` — MPxN, move-in date, and optionally address.
- `expressed-by` — whether the data subject or an authorised representative
  initiated the registration.
- `principal-verification` — identity verification evidence (method, outcome,
  reference). Strongly recommended; null if no structured verification was performed.
- `email` — stored as a one-way hash for future magic-link re-identification
  and lookup. Optional but required if magic-link re-identification is to be used.
- `initiate-passkey-registration` — if `true`, the response includes a
  `passkey-registration-redirect` containing a short-lived URL to
  `id.central.consent`. Redirect the customer to this URL — the register
  completes the WebAuthn ceremony on its own origin, stores the public key,
  and redirects back with `?dar-passkey-token={token-ref}`. Call
  `GET /identity-records/{ir}/re-identify/{token-ref}` once to confirm.
  Supply `passkey-return-url` to control where the customer lands after
  the ceremony.

Identity Records are scoped to the authenticated Data User and are never
exposed to the unauthenticated `GET /access-records/{ak}` endpoint used by
Data Providers.




## OpenAPI

````yaml /data-access-register/api/openapi-dar.yaml post /identity-records
openapi: 3.1.0
info:
  title: Data Access Register
  description: >
    Core APIs for the Central Data Access Register.


    The register records all lawful access to customer energy data by
    Controllers,

    regardless of the legal basis on which that access rests. It is not
    exclusively

    a consent register — legitimate interests, public task, legal obligation,
    and

    contract are all within scope alongside consent-based access.


    **Jurisdiction:** This is a UK-only solution. All Controllers, customers,
    and

    meter points are within Great Britain. The governing data protection
    framework

    is **UK GDPR** and the **Data Protection Act 2018**. EU GDPR does not apply.

    The jurisdiction field is fixed to `GB` and does not need to be supplied.


    ## Purpose


    The register provides a single, auditable source of truth for:


    - **Controllers** — to record and demonstrate the legal basis under which
    they
      access customer meter data.
    - **Data Providers** — to verify that a Controller has a registered, active
    access
      record before releasing data.
    - **Customers** — to view all parties registered as having lawful access to
    their
      meter point, on request via any Controller.

    ## Party Model


    | Party | UK GDPR Role | Description |

    |---|---|---|

    | **Data User** | Processor | Operates the register. Fetches and processes
    meter data on behalf of Controllers. Authenticated via bearer token. |

    | **Controller** | Data Controller | A B2B customer of the Data User. One or
    more Controllers may be registered per access record — sole, joint (Art.
    26), or group (trading name transparency). Bears GDPR accountability for the
    legal basis and its ongoing validity. |

    | **Customer** | Data Subject | The energy customer. Identified by MPxN to
    minimise PII held centrally. |

    | **Data Provider** | Data Source | Holds meter data. Verifies access
    records before releasing data via `GET /access-records/{ak}`. |


    ## Record Structure


    All access records share a single schema — `AccessRecord` — with four
    sections

    aligned to ISO/IEC TS 27560:2023:


    1. **`record-metadata`** — record identifier, creation timestamp, controller
       arrangement (sole, joint, or group), and a reference to the associated
       Identity Record. Present for all records.
    2. **`notice`** — the privacy notice(s) presented to the customer. Present
    only
       for consent-based records; null for all other legal bases. Supports a single
       shared notice (common) or per-controller notices (joint arrangements where
       controllers discharge Art. 13 obligations separately).
    3. **`processing`** — legal basis, purpose, data types, and related
    obligations.
       Present for all records. Per-controller compliance fields (privacy rights URL,
       LIA reference, statutory reference, storage conditions) are held on each
       controller entry within `record-metadata.controller-arrangement`.
    4. **`access-event`** — when and how access was registered or consent was
    obtained;
       current lifecycle state. Present for all records.

    The `notice` and `access-event.consent` sub-object are null for non-consent
    records.

    The `access-event.consent` sub-object is null for non-consent records and
    populated

    for consent records.


    ## Identity Records


    The person-property relationship — who the individual is and their occupancy
    of the

    meter point — is held separately in an **Identity Record**
    (`/identity-records`).

    This keeps PII (MPxN, address, move-in date) and identity verification
    evidence

    out of the access record entirely.


    Each `AccessRecord` carries an `identity-record-ref` (`ir` key) linking it
    to its

    Identity Record. Identity Records are accessible only to authenticated Data
    Users.

    The unauthenticated `GET /access-records/{ak}` endpoint used by Data
    Providers

    never returns PII.


    Identity Records support two re-identification flows to reconnect a
    returning

    customer to an existing record without re-collecting all their details:

    - **Magic link** — a short-lived email link triggers re-identification.

    - **Passkey** — initiates a WebAuthn registration or assertion ceremony.


    ## Legal Basis and Required Fields


    The `processing.legal-basis` value determines which fields within `notice`
    and

    `access-event.consent` are expected:


    | Legal basis | `notice` | `access-event.consent` |

    |---|---|---|

    | `uk-consent`, `uk-explicit-consent` | **Required** | **Required** |

    | All other bases | Must be null | Must be null |


    For legitimate interests records, `lia-reference` should be supplied on the

    lead controller entry within `record-metadata.controller-arrangement`.

    For public task or legal obligation records, `statutory-reference`

    should be supplied on the lead controller entry.


    ## Access Record Lifecycle


    Records are never hard-deleted. All state transitions are recorded with
    timestamps.


    ```

    [ACTIVE] ──revoke──▶ [REVOKED]

    [ACTIVE] ──expiry──▶ [EXPIRED]

    [DISCOVERED] ──claimed──▶ [ACTIVE]

    [DISCOVERED] ──expiry──▶ [EXPIRED]

    ```


    `REVOKED` represents consent withdrawal (consent records) or the Controller

    removing their registration (non-consent records). Data Providers must deny

    access for any record not in `ACTIVE` state with a future (or null) expiry.


    `DISCOVERED` is a sparse, read-only state created by the DCC when it detects

    historic meter data requests from an organisation that has not yet
    registered

    as a Data User. These records are visible to Customers for transparency but

    cannot be used to authorise data release. When the organisation onboards as

    a Data User and registers a full access record for the same MPxN, the

    DISCOVERED record is superseded and transitions to `ACTIVE`.


    ## Authentication


    All endpoints except `GET /access-records/{ak}` require a bearer token from

    `GET /auth/token`. Tokens expire after 7200 seconds.


    ## Versioning


    URI-based versioning. Current version: `v1`.
  version: 0.0.16
  contact:
    name: Auth Energy
    email: contact@auth.energy
    url: https://docs.auth.energy/consent
  license:
    name: Proprietary
servers:
  - url: https://api.central.consent/v1
    description: Production
security: []
tags:
  - name: Identity Records
    description: >
      Endpoints for Data Users to create and manage Identity Records, which hold
      the person-property relationship (MPxN, address, move-in date, identity
      verification evidence) separately from access records. Supports
      re-identification of returning customers via email magic link or WebAuthn
      passkey.
  - name: Re-identification
    description: >-
      Endpoints for initiating and confirming customer re-identification flows.
      Supports magic link, passkey assertion, and passkey registration — both
      for the same Data User (same-DUID) and for a second Data User linking to
      an existing Identity Record created by another Data User (cross-DUID). In
      the cross-DUID flow the `ir` key is never exposed to the initiating Data
      User — the register resolves it internally after the customer completes
      the challenge.
  - name: Authentication
    description: Obtain bearer tokens for authenticated endpoints.
  - name: Data Users
    description: >
      Endpoints for Data Users (Processors) to register and manage access
      records on behalf of their Controller customers.
  - name: Data Providers
    description: >
      Endpoints for Data Providers (EDPs) to verify access records and look up
      registered Data User organisations before releasing meter data. Includes
      access record verification via access key and Data User directory lookup
      by DUID, allowing EDPs to confirm both that a specific record is active
      and that the organisation behind it is in good standing.
  - name: DCC
    description: >
      Endpoints restricted to Smart DCC system credentials. Covers two
      responsibilities: (1) submitting discovered access records derived from
      historic meter transaction logs — sparse, read-only records that surface
      to customers for transparency but cannot authorise data release,
      superseded when the organisation registers as a Data User; and (2)
      submitting Change of Tenancy events, which trigger `tenancy.change`
      webhooks to all Data Users with active access records on the affected
      MPxN.
  - name: Customer Portal
    description: >
      The centralised Customer Consent Portal is a standalone web service at
      https://portal.central.consent. It has no API endpoints — Data Users link
      customers to the portal via a plain URL carrying `ir` or `mpxn` and an
      optional `return` parameter. The portal authenticates the customer
      directly using the re-identification mechanisms on their Identity Record
      (passkey, magic link, or supplier OAuth fallback). This tag documents the
      portal behaviour and the `consent.withdrawal` webhook it fires on
      revocation.
  - name: Webhooks
    description: >
      Endpoints for Controllers to register and manage webhook subscriptions.
      The register dispatches event payloads to registered callback URLs when
      consent records are approaching expiry or when a Change of Tenancy is
      recorded against an MPxN with active access records.
paths:
  /identity-records:
    post:
      tags:
        - Identity Records
      summary: Create an Identity Record
      description: >
        Creates an Identity Record holding the person-property relationship for
        a

        consent or access registration. Returns an `ir` key that must be
        supplied

        as `record-metadata.identity-record-ref` when creating the linked
        AccessRecord.


        The Identity Record holds:

        - `pii-principal` — MPxN, move-in date, and optionally address.

        - `expressed-by` — whether the data subject or an authorised
        representative
          initiated the registration.
        - `principal-verification` — identity verification evidence (method,
        outcome,
          reference). Strongly recommended; null if no structured verification was performed.
        - `email` — stored as a one-way hash for future magic-link
        re-identification
          and lookup. Optional but required if magic-link re-identification is to be used.
        - `initiate-passkey-registration` — if `true`, the response includes a
          `passkey-registration-redirect` containing a short-lived URL to
          `id.central.consent`. Redirect the customer to this URL — the register
          completes the WebAuthn ceremony on its own origin, stores the public key,
          and redirects back with `?dar-passkey-token={token-ref}`. Call
          `GET /identity-records/{ir}/re-identify/{token-ref}` once to confirm.
          Supply `passkey-return-url` to control where the customer lands after
          the ceremony.

        Identity Records are scoped to the authenticated Data User and are never

        exposed to the unauthenticated `GET /access-records/{ak}` endpoint used
        by

        Data Providers.
      operationId: createIdentityRecord
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CreateIdentityRecordRequest'
            examples:
              with-email-and-passkey:
                summary: With email and passkey registration initiated
                value:
                  pii-principal:
                    mpxn: '1234567890123'
                    move-in-date: '2022-06-30'
                    address:
                      addressLine1: 221B Baker Street
                      townCity: London
                      postcode: NW1 6XE
                  expressed-by: data-subject
                  email: customer@example.com
                  initiate-passkey-registration: true
                  principal-verification:
                    method: credit-card
                    verified-at: '2023-11-07T05:30:45Z'
                    outcome: verified
                    reference: ch_3ABC123xyz
                    submitted: XXXX-XXXX-XXXX-4242
                    verified-against: Stripe customer record cus_ABC123
                    detail:
                      last4: '4242'
                      brand: visa
                      stripe_customer: cus_ABC123
              email-only:
                summary: With email, no passkey
                value:
                  pii-principal:
                    mpxn: '1234567890123'
                    move-in-date: '2020-01-01'
                  expressed-by: data-subject
                  email: customer@example.com
                  principal-verification: null
              minimal:
                summary: No re-identification credentials
                value:
                  pii-principal:
                    mpxn: '1234567890123'
                    move-in-date: '2020-01-01'
                  expressed-by: data-subject
                  principal-verification: null
      responses:
        '201':
          description: >
            Identity record created successfully. If
            `initiate-passkey-registration`

            was `true`, `passkey-registration-redirect` is populated — redirect
            the

            customer to `redirect-url`. The register completes the WebAuthn
            ceremony

            on `id.central.consent` and sends the customer back to `return-url`
            with

            `?dar-passkey-token={token-ref}`. Call

            `GET /identity-records/{ir}/re-identify/{token-ref}` once to
            confirm.
          headers:
            Location:
              description: URL of the newly created identity record.
              schema:
                type: string
                example: /v1/identity-records/ir_a3c5e7f9b1d3a3c5e7f9b1d3
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/CreateIdentityRecordResponse'
              examples:
                with-passkey-redirect:
                  summary: Response when passkey registration was initiated
                  value:
                    response:
                      resource: /v1/identity-records/ir_a3c5e7f9b1d3a3c5e7f9b1d3
                      timestamp: '2024-01-15T09:30:00Z'
                      transaction-id: tid_691df0c788ca043403b7fa90
                    ir: ir_a3c5e7f9b1d3a3c5e7f9b1d3
                    passkey-registration-redirect:
                      redirect-url: >-
                        https://id.central.consent/passkey/register?session=pks_a1b2c3d4e5f6a1b2c3d4e5f6
                      token-ref: mlr_9f8e7d6c5b4a9f8e7d6c5b4a
                      expires-at: '2024-01-15T09:35:00Z'
                without-passkey-redirect:
                  summary: Response when no passkey registration requested
                  value:
                    response:
                      resource: /v1/identity-records/ir_a3c5e7f9b1d3a3c5e7f9b1d3
                      timestamp: '2024-01-15T09:30:00Z'
                      transaction-id: tid_691df0c788ca043403b7fa90
                    ir: ir_a3c5e7f9b1d3a3c5e7f9b1d3
                    passkey-registration-redirect: null
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorised'
        '403':
          $ref: '#/components/responses/Forbidden'
        '500':
          $ref: '#/components/responses/InternalServerError'
      security:
        - bearerAuth: []
components:
  schemas:
    CreateIdentityRecordRequest:
      type: object
      description: >
        Request body for `POST /identity-records`. Extends the core
        `IdentityRecord` fields with `email`, `initiate-passkey-registration`,
        and `passkey-return-url`.
      properties:
        pii-principal:
          $ref: '#/components/schemas/PIIPrincipal'
        expressed-by:
          $ref: '#/components/schemas/ExpressionByEntity'
          description: >
            Who expressed consent or initiated the access registration — the
            data subject themselves, or an authorised representative.
        principal-verification:
          nullable: true
          description: >
            How the Controller verified the identity of the principal. Strongly
            recommended. Null if no structured verification was performed.
          allOf:
            - $ref: '#/components/schemas/PrincipalVerification'
        email:
          type: string
          format: email
          nullable: true
          description: >
            Customer email address. Stored as a one-way hash — never returned in
            plaintext in any response. Required if magic-link re-identification
            is to be used. Also enables email-based lookup via `GET
            /identity-records?email=...`.
          example: customer@example.com
        initiate-passkey-registration:
          type: boolean
          default: false
          description: >
            If `true`, the register generates a passkey registration session and
            returns a `passkey-registration-redirect`. Redirect the customer to
            the supplied URL; the register completes the WebAuthn ceremony on
            `id.central.consent` and redirects back to `passkey-return-url` with
            `?dar-passkey-token={token-ref}` on success.
        passkey-return-url:
          type: string
          format: uri
          nullable: true
          description: >
            Required when `initiate-passkey-registration` is `true`. The URL the
            register redirects the customer to after the passkey ceremony
            completes. Must exactly match a URL pre-registered for the Data
            User's DUID. Ignored if `initiate-passkey-registration` is false or
            omitted.
          example: https://app.bright-energy.com/onboard/passkey-done
      required:
        - pii-principal
        - expressed-by
    CreateIdentityRecordResponse:
      type: object
      properties:
        response:
          $ref: '#/components/schemas/ResponseMetadata'
        ir:
          $ref: '#/components/schemas/IdentityRecordKey'
        passkey-registration-redirect:
          nullable: true
          description: >
            Populated when `initiate-passkey-registration` was `true`. Redirect
            the customer to `redirect-url` — the register runs the WebAuthn
            registration ceremony on `id.central.consent` and returns them to
            `return-url` with `?dar-passkey-token={token-ref}`. Call `GET
            /identity-records/{ir}/re-identify/{token-ref}` once to confirm.
            Null otherwise.
          allOf:
            - $ref: '#/components/schemas/PasskeyRedirect'
      required:
        - response
        - ir
    PIIPrincipal:
      type: object
      description: >
        Identifies the customer and their property. MPxN is the primary
        identifier; personal name and email are excluded to minimise PII held
        centrally.
      properties:
        mpxn:
          $ref: '#/components/schemas/MPxN'
        move-in-date:
          type: string
          format: date
          description: >
            Date the customer began occupying the property. Establishes the
            earliest point from which data access applies (inclusive).
          example: '2022-06-30'
        address:
          $ref: '#/components/schemas/UKAddress'
      required:
        - mpxn
        - move-in-date
    ExpressionByEntity:
      type: string
      description: >
        Who expressed consent. Only applicable when `legal-basis` is a consent
        value.


        - `data-subject` — the customer themselves.

        - `authorised-representative` — a third party acting on the customer's
        behalf
          (e.g. a landlord or property manager acting for a tenant).
      enum:
        - data-subject
        - authorised-representative
    PrincipalVerification:
      type: object
      description: >
        Records how the Controller verified the identity of the principal
        *before*

        consent was obtained.


        Identity verification and consent expression are distinct processes:

        - **Verification** confirms that the person is who they claim to be.

        - **Consent** records that the verified person agreed to processing.


        This object captures the verification event in a generic envelope
        applicable

        to all methods. Method-specific supplementary data that does not fit the

        standard fields may be placed in `detail`.


        `submitted` must always be redacted — the register must never hold a
        full

        card number, full account number, or other unredacted sensitive
        credential.
      properties:
        method:
          type: string
          maxLength: 100
          description: >
            The verification mechanism used to confirm the principal's identity
            (e.g. "credit-card", "account-postcode", "in-home-display",
            "gov-id-check", "existing-authenticated-session").
          example: credit-card
        verified-at:
          type: string
          format: date-time
          description: UTC timestamp of when the verification was performed.
          example: '2023-11-07T05:31:00Z'
        outcome:
          type: string
          enum:
            - verified
            - failed
            - partial
          description: >
            Result of the verification check.

            - `verified` — identity confirmed.

            - `failed` — verification was attempted but could not be confirmed.

            - `partial` — some signals matched but full verification was not
            achieved
              (e.g. postcode matched but name did not).
          example: verified
        reference:
          type: string
          maxLength: 255
          description: >
            Opaque reference to the verification event in the external system.
            Allows the Controller to look up the full verification record (e.g.
            Stripe charge ID, IHD session ID, CRM verification ticket number).
          example: ch_3ABC123xyz
        submitted:
          type: string
          maxLength: 255
          description: >
            Redacted representation of the credential or value submitted by the
            principal (e.g. "XXXX-XXXX-XXXX-4242", "postcode SW1A 1AA", "IHD
            code 6-digit entry"). Must never contain unredacted sensitive data.
          example: XXXX-XXXX-XXXX-4242
        verified-against:
          type: string
          maxLength: 255
          description: >
            Description of what the submitted value was checked against (e.g.
            "Stripe customer record cus_ABC123", "Smart meter pairing code
            issued by DCC", "Utility account postcode on file in CRM").
          example: Stripe customer record cus_ABC123
        detail:
          type: object
          additionalProperties: true
          description: >
            Freeform object for method-specific supplementary data not captured
            by the standard fields. Controllers may include any additional
            verification metadata here (e.g. card brand, device ID, assurance
            level).
          example:
            last4: '4242'
            brand: visa
            stripe_customer: cus_ABC123
      required:
        - method
        - verified-at
        - outcome
    ResponseMetadata:
      type: object
      properties:
        resource:
          type: string
          example: /v1/access-records/ak_691df0c788ca043403b7fa90
        timestamp:
          type: string
          format: date-time
          example: '2026-03-11T12:00:00Z'
        transaction-id:
          type: string
          description: >-
            Unique identifier for this request/response pair. Quote in support
            enquiries.
          example: tid_691df0c788ca043403b7fa90
      required:
        - resource
        - timestamp
        - transaction-id
    IdentityRecordKey:
      type: string
      pattern: ^ir_[0-9a-f]{24}$
      description: >
        Unique opaque identifier for an Identity Record, issued by the register
        on creation. Referenced from `record-metadata.identity-record-ref` on an
        AccessRecord to link the two resources.
      example: ir_a3c5e7f9b1d3a3c5e7f9b1d3
    PasskeyRedirect:
      type: object
      description: >
        A short-lived redirect to `id.central.consent` where the register
        completes the WebAuthn ceremony on its own origin. Present when a
        passkey method was initiated; null otherwise. Redirect the customer to
        `redirect-url` immediately — the session expires in 5 minutes.

        On completion, the register redirects the customer back to `return-url`
        (if supplied) with `?dar-passkey-token={token-ref}` appended. Call `GET
        /identity-records/{ir}/re-identify/{token-ref}` once to confirm.
      properties:
        redirect-url:
          type: string
          format: uri
          description: >
            Short-lived URL to `id.central.consent` that initiates the WebAuthn
            ceremony. Valid for 5 minutes; single-use.
          example: >-
            https://id.central.consent/passkey/assert?session=pks_a1b2c3d4e5f6a1b2c3d4e5f6
        token-ref:
          type: string
          pattern: ^mlr_[0-9a-f]{24}$
          description: >
            Opaque reference to confirm completion. The register appends
            `?dar-passkey-token={token-ref}` to the `return-url` after the
            ceremony. Call `GET /identity-records/{ir}/re-identify/{token-ref}`
            once to verify `status: confirmed`.
          example: mlr_1a2b3c4d5e6f1a2b3c4d5e6f
        expires-at:
          type: string
          format: date-time
          description: UTC timestamp when the redirect URL expires.
          example: '2026-03-24T09:05:00Z'
        return-url:
          type: string
          format: uri
          nullable: true
          description: >
            The registered return URL supplied in the request, if any. Echoed
            back for confirmation. Null when no `passkey-return-url` was
            supplied.
          example: https://app.bright-energy.com/renew/confirmed
      required:
        - redirect-url
        - token-ref
        - expires-at
    ErrorResponse:
      type: object
      properties:
        response:
          $ref: '#/components/schemas/ResponseMetadata'
        errors:
          type: array
          minItems: 1
          items:
            $ref: '#/components/schemas/ErrorDetail'
      required:
        - response
        - errors
    MPxN:
      type: string
      pattern: ^(?:[0-9A-HJ-NPR-Z]{2}[0-9]{8,10}|[0-9]{10})$
      description: >-
        Meter Point Administration Number (MPAN) or Meter Point Reference Number
        (MPRN).
      example: '1234567890123'
    UKAddress:
      type: object
      properties:
        addressLine1:
          type: string
          maxLength: 100
          example: 221B Baker Street
        addressLine2:
          type: string
          maxLength: 100
          nullable: true
          example: Marylebone
        townCity:
          type: string
          maxLength: 100
          example: London
        county:
          type: string
          maxLength: 100
          nullable: true
          example: Greater London
        postcode:
          type: string
          pattern: ^[A-Z]{1,2}[0-9][0-9A-Z]? ?[0-9][A-Z]{2}$
          example: NW1 6XE
      required:
        - addressLine1
        - townCity
        - postcode
    ErrorDetail:
      type: object
      properties:
        error-code:
          type: string
          example: VAL001
        message:
          type: string
          example: Field 'mpxn' does not match the required pattern.
      required:
        - error-code
        - message
  responses:
    BadRequest:
      description: The request was malformed or failed validation.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorResponse'
    Unauthorised:
      description: Missing or invalid bearer token.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorResponse'
    Forbidden:
      description: Authenticated caller lacks permission for this action.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorResponse'
    InternalServerError:
      description: An unexpected server-side error occurred.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorResponse'
  securitySchemes:
    bearerAuth:
      type: http
      scheme: bearer
      bearerFormat: JWT
      description: >-
        JWT from `GET /auth/token`. Pass as `Authorization: Bearer <token>`.
        Expires after 7200s.

````