> ## 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.

# Register a Webhook Subscription

> Registers an HTTPS callback URL to receive lifecycle event notifications
dispatched by the register.

**Two event types** are delivered to the same callback URL, distinguished
by the `event-type` field in the payload envelope:

- `consent.expiring` — fired when a consent-based access record is within
  the `notify-days-before` window of its `access-event.expiry` date. Allows
  Controllers to prompt the customer to renew before their access lapses.
  Only fired for records where `access-event.expiry` is non-null and
  `access-event.state` is `ACTIVE`.

- `tenancy.change` — fired when the register receives notification that a
  Change of Tenancy has been recorded against an MPxN that has one or more
  `ACTIVE` access records. Controllers should use this to halt data
  collection for the departing occupant and, if appropriate, initiate a
  fresh consent or access registration for the new occupant. The event
  carries only the MPxN and the effective date of the change — no new
  occupant PII is included.

**Delivery semantics:**
- The register performs an HTTPS POST to the `callback-url` with the event
  payload as the request body.
- The callback endpoint must respond with HTTP `2xx` within 10 seconds.
- On failure the register retries with exponential backoff: 1 min, 5 min,
  30 min, 2 hr, 24 hr. After 5 failed attempts the event is marked
  `undelivered` and the Data User is notified via the registered
  `alert-email`.
- The register signs each delivery with an HMAC-SHA256 signature over the
  raw request body, using the `signing-secret` returned at registration.
  The signature is passed in the `X-DAR-Signature` header as
  `sha256=<hex-digest>`. Receivers must verify this before processing.

**Filtering:** By default, subscriptions receive all event types for all
access records registered under the authenticated Data User. Supply
`event-types` to restrict to a specific subset.

**Idempotency:** Registering a second subscription with the same
`callback-url` returns `409 Conflict`. Update an existing subscription
using `PATCH /webhooks/{wid}`.




## OpenAPI

````yaml /data-access-register/api/openapi-dar.yaml post /webhooks
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:
  /webhooks:
    post:
      tags:
        - Webhooks
      summary: Register a Webhook Subscription
      description: >
        Registers an HTTPS callback URL to receive lifecycle event notifications

        dispatched by the register.


        **Two event types** are delivered to the same callback URL,
        distinguished

        by the `event-type` field in the payload envelope:


        - `consent.expiring` — fired when a consent-based access record is
        within
          the `notify-days-before` window of its `access-event.expiry` date. Allows
          Controllers to prompt the customer to renew before their access lapses.
          Only fired for records where `access-event.expiry` is non-null and
          `access-event.state` is `ACTIVE`.

        - `tenancy.change` — fired when the register receives notification that
        a
          Change of Tenancy has been recorded against an MPxN that has one or more
          `ACTIVE` access records. Controllers should use this to halt data
          collection for the departing occupant and, if appropriate, initiate a
          fresh consent or access registration for the new occupant. The event
          carries only the MPxN and the effective date of the change — no new
          occupant PII is included.

        **Delivery semantics:**

        - The register performs an HTTPS POST to the `callback-url` with the
        event
          payload as the request body.
        - The callback endpoint must respond with HTTP `2xx` within 10 seconds.

        - On failure the register retries with exponential backoff: 1 min, 5
        min,
          30 min, 2 hr, 24 hr. After 5 failed attempts the event is marked
          `undelivered` and the Data User is notified via the registered
          `alert-email`.
        - The register signs each delivery with an HMAC-SHA256 signature over
        the
          raw request body, using the `signing-secret` returned at registration.
          The signature is passed in the `X-DAR-Signature` header as
          `sha256=<hex-digest>`. Receivers must verify this before processing.

        **Filtering:** By default, subscriptions receive all event types for all

        access records registered under the authenticated Data User. Supply

        `event-types` to restrict to a specific subset.


        **Idempotency:** Registering a second subscription with the same

        `callback-url` returns `409 Conflict`. Update an existing subscription

        using `PATCH /webhooks/{wid}`.
      operationId: registerWebhook
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/WebhookRegistrationRequest'
            example:
              callback-url: https://app.bright-energy.com/webhooks/dar
              alert-email: platform-ops@bright-energy.com
              notify-days-before: 30
              event-types:
                - consent.expiring
                - tenancy.change
      responses:
        '201':
          description: Webhook subscription created.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/RegisterWebhookResponse'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorised'
        '409':
          description: >
            A subscription with the supplied `callback-url` already exists for
            this Data User. Use `PATCH /webhooks/{wid}` to update it.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '500':
          $ref: '#/components/responses/InternalServerError'
      security:
        - bearerAuth: []
components:
  schemas:
    WebhookRegistrationRequest:
      type: object
      properties:
        callback-url:
          type: string
          format: uri
          description: >
            HTTPS endpoint that will receive event POST requests. HTTP (non-TLS)
            URLs are rejected. The endpoint must return HTTP `2xx` within 10
            seconds.
          example: https://app.bright-energy.com/webhooks/dar
        alert-email:
          type: string
          format: email
          description: >
            Email address to notify if repeated delivery failures occur (after
            the final retry attempt). Should be a monitored operations mailbox.
          example: platform-ops@bright-energy.com
        notify-days-before:
          type: integer
          minimum: 1
          maximum: 90
          default: 30
          description: >
            How many days before `access-event.expiry` the register should fire
            a `consent.expiring` event. Only applies to `consent.expiring`
            events. Default is 30 days if omitted.
          example: 30
        event-types:
          type: array
          minItems: 1
          items:
            $ref: '#/components/schemas/WebhookEventType'
          default:
            - consent.expiring
            - tenancy.change
            - consent.withdrawal
          description: >
            Event types this subscription should receive. Defaults to all types
            if omitted.
      required:
        - callback-url
        - alert-email
    RegisterWebhookResponse:
      type: object
      properties:
        response:
          $ref: '#/components/schemas/ResponseMetadata'
        webhook:
          $ref: '#/components/schemas/WebhookSubscription'
        signing-secret:
          type: string
          description: >
            HMAC-SHA256 signing secret for this subscription. Returned only on
            creation (`POST`) or when `rotate-secret: true` is supplied on
            update. Store securely — this value is not retrievable after the
            response is returned.
          example: whsec_a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4
      required:
        - response
        - webhook
    ErrorResponse:
      type: object
      properties:
        response:
          $ref: '#/components/schemas/ResponseMetadata'
        errors:
          type: array
          minItems: 1
          items:
            $ref: '#/components/schemas/ErrorDetail'
      required:
        - response
        - errors
    WebhookEventType:
      type: string
      description: |
        The event type delivered to the webhook callback URL.

        - `consent.expiring` — a consent-based access record is within the
          configured `notify-days-before` window of its expiry date.
        - `tenancy.change` — a Change of Tenancy has been recorded against an
          MPxN that has one or more `ACTIVE` access records registered under
          this Data User.
        - `consent.withdrawal` — a customer has withdrawn consent via the
          centralised Customer Consent Portal. The access record has already
          transitioned to `REVOKED` state before this event is fired. Data
          Users must update their own systems immediately on receipt.
      enum:
        - consent.expiring
        - tenancy.change
        - consent.withdrawal
    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
    WebhookSubscription:
      type: object
      description: A registered webhook subscription as returned by the API.
      properties:
        wid:
          $ref: '#/components/schemas/WebhookId'
        callback-url:
          type: string
          format: uri
          example: https://app.bright-energy.com/webhooks/dar
        alert-email:
          type: string
          format: email
          example: platform-ops@bright-energy.com
        notify-days-before:
          type: integer
          example: 30
        event-types:
          type: array
          items:
            $ref: '#/components/schemas/WebhookEventType'
        created-at:
          type: string
          format: date-time
          example: '2026-01-10T08:00:00Z'
        active:
          type: boolean
          description: >-
            Whether the subscription is currently active. Inactive subscriptions
            receive no deliveries.
      required:
        - wid
        - callback-url
        - alert-email
        - notify-days-before
        - event-types
        - created-at
        - active
    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
    WebhookId:
      type: string
      pattern: ^wid_[0-9a-f]{24}$
      description: Unique identifier for a webhook subscription.
      example: wid_a1b2c3d4e5f6a1b2c3d4e5f6
  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'
    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.

````