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

# Onboard

> Initialise a site and its assets, and enrol them in a proposition in a single call.

This endpoint will:
1. Upsert the site and asset(s)
2. Enrol in the relevant flex proposition

The response returns the site and asset IDs you'll use in subsequent calls, and the enrolment outcome.

Onboarding is idempotent and atomic — sending the same site or asset returns the existing record with any new fields merged in; if enrolment fails, nothing is stored.

A 200 response always means the site and assets exist and the site is enrolled; `enrolment.status` only distinguishes a new enrolment from an existing one. Any failure returns an HTTP error with nothing persisted, so fix the cause and send the same request again.

<Note>
  Already integrated against [`/onboard-site-and-asset`](./onboard-site-and-asset)?
  It's still supported, but we'd recommend migrating to `/onboard` when convenient.
</Note>

## Handling failures

A `200` response always means the site and assets were stored and the site is enrolled.
`enrolment.status` only tells you whether the enrolment was new (`enrolled`) or already in
place (`already_enrolled`).

An unsuccessful onboarding returns an HTTP error instead. The call is atomic, so on any error
nothing is persisted: no site, no assets, no enrolment. Fix the cause and send the same request
again.

| Status | Meaning | What to do |
| - | - | - |
| `400` | Invalid request: missing `gave_boundary_meter_consent_at`, an invalid email, or no boundary consent terms registered for your organisation. `detail` is a plain message. | Correct the request. If the message mentions terms and conditions, contact Axle. |
| `404` | A referenced entity (such as an `asset_model`) doesn't exist or isn't visible to your organisation. | Check the identifiers. |
| `409` | An asset with this `external_id` already exists. `detail.message` says why and `detail.existing_asset_id` identifies it. | Registered to a different site: [offboard](./offboard) the old site first, or use a different `external_id`. Different type, or owned by another organisation: contact Axle. |
| `422` with `detail.eligibility` | The site or assets don't meet the proposition's prerequisites. `detail.eligibility` holds the per-site and per-asset breakdown. | Resolve the missing prerequisite, or use [check eligibility](./eligibility) up front. |
| `422` with `detail.code: "previously_withdrew"` | The site previously withdrew from this proposition, for example via [unenrol](./unenrol). | Resend with `override_withdrawal: true`. Recommended whenever the user asked to re-enrol. |
| `500` | Something went wrong on our side. | Retry; if it persists, contact Axle. |

Rate limiting and request-body validation errors behave as on every other endpoint.


## OpenAPI

````yaml POST /entities/site/onboard
openapi: 3.1.0
info:
  title: Axle API Docs
  description: >-
    For full documentation please see our official docs at <a
    href='http://docs.axle.energy'>docs.axle.energy</a>
  version: 1.4.6
servers:
  - url: https://api-sandbox.axle.energy
security: []
tags:
  - name: 1. Authentication
    description: Authenticate yourself with the Axle API
  - name: 2. Meters
    description: Find key info about your sites
  - name: 3. Sites
    description: Register your sites with Axle; configure market participation
  - name: 4. Assets
    description: Register your assets with Axle
  - name: 5. Data
    description: Send asset metrics to Axle, for analysis and optimisation purposes
  - name: 6. Rewards
    description: >-
      Determine how much your users have earned by participating with Axle;
      allow balance withdrawal
  - name: 7. Validation
    description: Validate structured data against Axle's rules
  - name: 8. Examples
    description: Get example data for testing
paths:
  /entities/site/onboard:
    post:
      tags:
        - 3. Sites
      summary: Onboard
      description: >-
        Initialise a site and its assets, and enrol them in a proposition in a
        single call.


        This endpoint will:

        1. Upsert the site and asset(s)

        2. Enrol in the relevant flex proposition


        The response returns the site and asset IDs you'll use in subsequent
        calls, and the enrolment outcome.


        Onboarding is idempotent and atomic — sending the same site or asset
        returns the existing record with any new fields merged in; if enrolment
        fails, nothing is stored.


        A 200 response always means the site and assets exist and the site is
        enrolled; `enrolment.status` only distinguishes a new enrolment from an
        existing one. Any failure returns an HTTP error with nothing persisted,
        so fix the cause and send the same request again.
      operationId: onboard_entities_site_onboard_post
      requestBody:
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/OnboardRequest'
        required: true
      responses:
        '200':
          description: Site and assets persisted, site enrolled in proposition
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/OnboardResponse'
              example:
                site:
                  site_id: a1b2c3d4-e5f6-7890-abcd-ef1234567890
                  site_created: true
                  fields_updated: []
                  warnings: []
                assets:
                  - asset_id: b2c3d4e5-f6a7-8901-bcde-f23456789012
                    external_id: charger-001
                    asset_created: true
                    fields_updated: []
                    warnings: []
                enrolment:
                  site_id: a1b2c3d4-e5f6-7890-abcd-ef1234567890
                  status: enrolled
        '400':
          description: >-
            Invalid request: missing boundary meter consent, invalid email, or
            no boundary consent terms registered for your organisation (contact
            Axle).
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorWithDetail'
        '404':
          description: Referenced entity not found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorWithDetail'
        '409':
          description: >-
            Asset conflict: an asset with this `external_id` is registered to a
            different site, used to exist as a different type, or belongs to
            another organisation.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/AssetConflictResponse'
        '422':
          description: >-
            Enrolment refused. Either eligibility prerequisites are not met
            (`detail.eligibility` holds the per-site and per-asset breakdown) or
            the site previously withdrew from this proposition (`detail.code` is
            `previously_withdrew`; resend with `override_withdrawal: true`).
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/EnrolRejectedResponse'
        '500':
          description: Server error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorWithDetail'
      security:
        - OAuth2PasswordBearer: []
components:
  schemas:
    OnboardRequest:
      properties:
        site:
          allOf:
            - $ref: '#/components/schemas/SiteRequest'
          description: Site information
          examples:
            - email: resident@example.com
              gave_boundary_meter_consent_at: '2026-01-15T10:30:00Z'
              mpan: '1234567890123'
              postcode: SW1A 1AA
              street_address: 10 Downing Street
        assets:
          items:
            $ref: '#/components/schemas/OnboardingAssetRequest'
          type: array
          title: Assets
          description: One or more assets to register at the site
          examples:
            - - asset_model: zappi
                external_id: charger-001
                installation_date: '2025-06-15'
                properties:
                  power_kw: 7.4
                type: charger
        proposition:
          type: string
          enum:
            - limited_pause
            - full_asset_schedule_control
            - price_curve
          title: Proposition
          description: Proposition to enrol the site in
          examples:
            - limited_pause
        override_withdrawal:
          type: boolean
          title: Override Withdrawal
          description: Set to true to re-enrol regardless of previous withdrawals.
          default: false
          examples:
            - false
      type: object
      required:
        - site
        - assets
        - proposition
      title: OnboardRequest
      description: Request model for the onboard endpoint.
    OnboardResponse:
      properties:
        site:
          allOf:
            - $ref: '#/components/schemas/InitialiseSiteResult'
          description: Site result metadata
        assets:
          items:
            $ref: '#/components/schemas/InitialiseAssetResult'
          type: array
          title: Assets
          description: Per-asset result metadata
        enrolment:
          allOf:
            - $ref: '#/components/schemas/EnrolResponse'
          description: Enrolment result
      type: object
      required:
        - site
        - assets
        - enrolment
      title: OnboardResponse
      description: Response model for the onboard endpoint.
    ErrorWithDetail:
      properties:
        detail:
          type: string
          title: Detail
      type: object
      required:
        - detail
      title: ErrorWithDetail
      description: Standard error response with a detailed message.
    AssetConflictResponse:
      properties:
        detail:
          $ref: '#/components/schemas/AssetConflictDetail'
      type: object
      required:
        - detail
      title: AssetConflictResponse
      description: >-
        409 body returned when an asset in the request conflicts with an
        existing one. Nothing is persisted.
    EnrolRejectedResponse:
      properties:
        detail:
          $ref: '#/components/schemas/EnrolRejectedDetail'
      type: object
      required:
        - detail
      title: EnrolRejectedResponse
      description: 422 body returned when an enrolment is refused. Nothing is persisted.
    SiteRequest:
      properties:
        street_address:
          type: string
          title: Street Address
          description: Street address for the site
        email:
          type: string
          title: Email
          description: >-
            Email address for the site. Include to enable 2FA for payment
            withdrawals.
        gave_boundary_meter_consent_at:
          type: string
          format: date-time
          title: Gave Boundary Meter Consent At
          description: >-
            Timezone-aware ISO 8601 timestamp (e.g. '2026-01-01T12:34:56Z')
            indicating when the household gave consent for Axle to access
            readings from their boundary meter, if consent was given.
        mpan:
          type: string
          title: Mpan
          description: Meter Point Administration Number
        postcode:
          type: string
          title: Postcode
          description: UK postcode for the site
      type: object
      required:
        - mpan
        - postcode
      title: SiteRequest
      description: Request model for creating a new site.
    OnboardingAssetRequest:
      properties:
        external_id:
          type: string
          title: External Id
          description: Vendor-specific identifier for the asset
          examples:
            - charger-001
        type:
          allOf:
            - $ref: '#/components/schemas/CreatableAssetType'
          description: Type of asset being created
          examples:
            - charger
        properties:
          allOf:
            - $ref: '#/components/schemas/AssetProperties'
          description: Optional asset properties
          examples:
            - power_kw: 7.4
        asset_model:
          type: string
          title: Asset Model
          description: >-
            Name of the AssetModel to assign (looked up by name). Required for
            CoP11 eligibility.
          examples:
            - axle_charger_v1
        installation_date:
          type: string
          format: date
          title: Installation Date
          description: >-
            Date when the asset was installed. Required alongside asset_model
            for CoP11 eligibility.
          examples:
            - '2025-06-15'
      type: object
      required:
        - external_id
        - type
      title: OnboardingAssetRequest
      description: Request model for creating an asset during onboarding (without site_id).
    InitialiseSiteResult:
      properties:
        site_id:
          type: string
          format: uuid
          title: Site Id
          description: Axle's internal UUID for the site
        site_created:
          type: boolean
          title: Site Created
          description: True if the site was newly created, False if it was upserted
        fields_updated:
          items:
            type: string
          type: array
          title: Fields Updated
          description: List of site fields that were updated on upsert
        warnings:
          items:
            type: string
          type: array
          title: Warnings
          description: Site-level warnings generated during initialisation
      type: object
      required:
        - site_id
        - site_created
      title: InitialiseSiteResult
      description: Result metadata for a site after upsert.
    InitialiseAssetResult:
      properties:
        asset_id:
          type: string
          format: uuid
          title: Asset Id
          description: Axle's internal UUID for the asset
        external_id:
          type: string
          title: External Id
          description: Vendor-specific identifier for the asset
        asset_created:
          type: boolean
          title: Asset Created
          description: True if the asset was newly created, False if it was upserted
        fields_updated:
          items:
            type: string
          type: array
          title: Fields Updated
          description: List of fields that were updated on upsert
        warnings:
          items:
            type: string
          type: array
          title: Warnings
          description: Per-asset warnings generated during initialisation
      type: object
      required:
        - asset_id
        - external_id
        - asset_created
      title: InitialiseAssetResult
      description: Result metadata for a single asset after upsert.
    EnrolResponse:
      properties:
        site_id:
          type: string
          format: uuid
          title: Site Id
          description: ID of the enrolled site
          examples:
            - a1b2c3d4-e5f6-7890-abcd-ef1234567890
        status:
          type: string
          enum:
            - enrolled
            - already_enrolled
          title: Status
          description: >-
            Outcome of the enrolment attempt. Both `enrolled` and
            `already_enrolled` indicate success.
          examples:
            - enrolled
      type: object
      required:
        - site_id
        - status
      title: EnrolResponse
      description: Outcome of an enrolment attempt.
    AssetConflictDetail:
      properties:
        message:
          type: string
          title: Message
          examples:
            - >-
              Asset with external_id charger-001 already exists with id
              b2c3d4e5-f6a7-8901-bcde-f23456789012. Asset charger-001 is
              registered to a different site
        existing_asset_id:
          type: string
          format: uuid
          title: Existing Asset Id
          description: ID of the conflicting asset
          examples:
            - b2c3d4e5-f6a7-8901-bcde-f23456789012
        external_id:
          type: string
          title: External Id
          description: The `external_id` that conflicted
          examples:
            - charger-001
      type: object
      required:
        - message
        - existing_asset_id
        - external_id
      title: AssetConflictDetail
      description: >-
        An asset with this `external_id` already exists: at a different site, as
        a different type, or in another org.
    EnrolRejectedDetail:
      properties:
        message:
          type: string
          title: Message
          description: Human-readable reason the enrolment was refused
          examples:
            - 'Prerequisites not met for proposition: limited_pause'
        code:
          type: string
          const: previously_withdrew
          title: Code
          description: >-
            Set when the site previously withdrew from this proposition. Resend
            with `override_withdrawal: true` to re-enrol.
        eligibility:
          allOf:
            - $ref: '#/components/schemas/SiteEligibilityDetail'
          description: >-
            Set when eligibility prerequisites are not met: the per-site and
            per-asset breakdown.
      type: object
      required:
        - message
      title: EnrolRejectedDetail
      description: >-
        Why an enrolment was refused: `code` for a prior withdrawal,
        `eligibility` for unmet prerequisites.
    CreatableAssetType:
      type: string
      enum:
        - charger
        - battery
        - heat pump
        - electric_vehicle
        - hot water tank
      title: CreatableAssetType
      description: Asset types that can be created through the API.
    AssetProperties:
      properties:
        power_kw:
          type: number
          minimum: 0
          title: Power Kw
          description: Power rating in kW
        capacity_kwh:
          type: number
          minimum: 0
          title: Capacity Kwh
          description: Capacity in kWh
      type: object
      title: AssetProperties
      description: Optional properties for an asset.
    SiteEligibilityDetail:
      properties:
        eligible_propositions:
          items:
            type: string
            enum:
              - limited_pause
              - full_asset_schedule_control
              - price_curve
          type: array
          title: Eligible Propositions
          default: []
        site_id:
          type: string
          format: uuid
          title: Site Id
          examples:
            - a1b2c3d4-e5f6-7890-abcd-ef1234567890
        site_properties:
          allOf:
            - $ref: '#/components/schemas/SiteProperties'
          examples:
            - has_meter_consent: true
              is_half_hourly_settled: false
        assets:
          items:
            $ref: '#/components/schemas/AssetEligibilityDetail'
          type: array
          title: Assets
          default: []
      type: object
      required:
        - site_id
        - site_properties
      title: SiteEligibilityDetail
      description: Eligibility detail for a site's proposition eligibility check.
    SiteProperties:
      properties:
        is_half_hourly_settled:
          type: boolean
          title: Is Half Hourly Settled
        has_meter_consent:
          type: boolean
          title: Has Meter Consent
      type: object
      required:
        - is_half_hourly_settled
        - has_meter_consent
      title: SiteProperties
      description: Site-level properties relevant to eligibility evaluation.
    AssetEligibilityDetail:
      properties:
        asset_id:
          type: string
          format: uuid
          title: Asset Id
          examples:
            - b2c3d4e5-f6a7-8901-bcde-f23456789012
        external_id:
          type: string
          title: External Id
          examples:
            - charger-001
        propositions:
          items:
            $ref: '#/components/schemas/PropositionEligibility'
          type: array
          title: Propositions
          default: []
      type: object
      required:
        - asset_id
        - external_id
      title: AssetEligibilityDetail
      description: Eligibility detail for a single asset across all propositions.
    PropositionEligibility:
      properties:
        proposition:
          type: string
          enum:
            - limited_pause
            - full_asset_schedule_control
            - price_curve
          title: Proposition
          examples:
            - limited_pause
        is_eligible:
          type: boolean
          title: Is Eligible
        issues:
          items:
            $ref: '#/components/schemas/EligibilityCode'
          type: array
          title: Issues
          default: []
      type: object
      required:
        - proposition
        - is_eligible
      title: PropositionEligibility
      description: Eligibility detail for a single proposition.
    EligibilityCode:
      type: string
      enum:
        - asset_model_not_set
        - asset_model_not_found
        - asset_model_not_cop11_qualified
        - asset_installation_date_not_set
        - asset_model_enrol_eligibility_override
        - non_domestic_site
        - mpan_not_set
        - meter_id_not_set
        - meter_id_not_found
        - postcode_not_in_eligible_area
      title: EligibilityCode
      description: Codes identifying specific eligibility issues for an asset.
  securitySchemes:
    OAuth2PasswordBearer:
      type: oauth2
      flows:
        password:
          scopes: {}
          tokenUrl: auth/token-form

````

This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.