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

# Discharge the battery

> Discharges the battery at the highest power the device allows. Energy not
consumed on site is exported to the grid, within any export limit applied by the
manufacturer or the installation. Discharging stops when stored energy reaches
`min_wh`, or at `end`.




## OpenAPI

````yaml /openapi.yaml post /bat/v1/{id}/discharge
openapi: 3.1.0
info:
  title: Kapa Energy Battery API
  version: 1.0.0-draft.1
  summary: One API to control and monitor residential batteries across manufacturers.
  description: >
    The Kapa Energy Battery API gives HEMS and VPP operators a single interface
    to send

    commands to residential batteries and read their telemetry, whatever the

    manufacturer. Kapa translates each request into the manufacturer's own cloud
    API.


    ## Conventions


    ### Time

    - Timestamps are RFC 3339, in UTC, with a `Z` suffix:
    `2026-11-12T18:00:00Z`.

    - Durations are ISO 8601: `PT15M`.

    - Time windows are half-open, `[start, end)`: `start` is included, `end` is
    excluded.


    ### Power

    Power is in watts (W), as integers.

    - **Positive (+)**: the battery is discharging, or the site is exporting to
    the grid.

    - **Negative (−)**: the battery is charging, or the site is importing from
    the grid.


    Nominal ratings such as `nominal_charge_power_w` are magnitudes and are
    always positive.


    ### Energy

    Energy is in watt-hours (Wh), as integers. Battery levels are expressed as
    stored

    energy in Wh rather than state of charge in %, because the meaning of SoC
    differs

    between manufacturers. Stored energy is measured against the usable
    capacity: it

    ranges from 0 to `usable_capacity_wh`.


    ### Telemetry

    - Each data point carries two times: `measured_at`, when the device took the
      measurement, and `received_at`, when Kapa retrieved it from the manufacturer's cloud.
    - Data points are never interpolated or resampled. Each point is an actual
      measurement reported by the manufacturer. Units, signs and field names are
      standardised across manufacturers; a field the manufacturer does not report is `null`.

    ## Commands

    - Commands are asynchronous. Command endpoints return `202 Accepted` with a
    command
      whose `status` then evolves. Follow it with `GET /bat/v1/{id}/commands` or the
      `command.status_changed` webhook.
    - A `start` in the past is treated as the time Kapa accepts the command.

    - **Overlaps:** when a new command overlaps existing commands on the same
    battery,
      the newest command takes precedence over the overlapping period. Older commands
      stay in effect outside it. A command whose whole window is covered by newer
      commands becomes `superseded`.
    - Outside any command window, the battery follows its **default behaviour**:
    the
      mode its owner configured in the manufacturer's app.

    ## Idempotency

    Command and batch endpoints accept an optional `Idempotency-Key` header.
    Kapa stores

    the key with the response for 24 hours. A retry with the same key and the
    same body

    returns the original response without sending anything again.


    ## Errors

    Errors use RFC 9457 Problem Details (`application/problem+json`). The `code`
    field is

    stable and meant for programs; `title` and `detail` are meant for humans.


    | `code` | HTTP | Meaning |

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

    | `invalid_parameters` | 400 | A parameter is missing, malformed or
    inconsistent (e.g. `min_wh` greater than `max_wh`). |

    | `invalid_time_window` | 400 | `start` is not before `end`, or `end` is in
    the past. |

    | `overlapping_operations` | 400 | Two operations of a batch target the same
    battery over overlapping windows. |

    | `unauthorized` | 401 | The access token is missing, invalid or expired. |

    | `battery_not_found` | 404 | No battery with this id is accessible to the
    client. |

    | `idempotency_key_in_use` | 409 | A request with the same `Idempotency-Key`
    is still being processed. |

    | `action_not_supported` | 422 | The battery cannot apply this command or
    parameter (e.g. `allow_import: false`). |

    | `energy_out_of_range` | 422 | An energy level exceeds the battery's usable
    capacity. |

    | `idempotency_key_reused` | 422 | The `Idempotency-Key` was already used
    with a different request body. |


    ## Webhooks

    Kapa pushes telemetry and command status changes to an HTTPS endpoint.
    Delivery is

    at least once and not ordered: deduplicate with `event_id`, and order data
    with

    `measured_at` or `updated_at`. Webhook registration is not covered by this
    version

    of the API.
servers:
  - url: https://api.kapa.energy
    description: Production
security:
  - oauth2: []
tags:
  - name: Authentication
    description: Obtain an OAuth 2.0 access token.
  - name: Batteries
    description: Static information about a battery.
  - name: Commands
    description: Control a single battery and follow the status of its commands.
  - name: Batches
    description: Send commands to many batteries in one request.
  - name: Telemetry
    description: Read measurements, standardised across manufacturers.
paths:
  /bat/v1/{id}/discharge:
    parameters:
      - $ref: '#/components/parameters/BatteryId'
    post:
      tags:
        - Commands
      summary: Discharge the battery
      description: >
        Discharges the battery at the highest power the device allows. Energy
        not

        consumed on site is exported to the grid, within any export limit
        applied by the

        manufacturer or the installation. Discharging stops when stored energy
        reaches

        `min_wh`, or at `end`.
      operationId: dischargeBattery
      parameters:
        - $ref: '#/components/parameters/IdempotencyKey'
      requestBody:
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/DischargeRequest'
            example:
              start: '2026-11-12T18:00:00Z'
              end: '2026-11-12T19:00:00Z'
              min_wh: 1500
      responses:
        '202':
          description: Command accepted. Its status then evolves asynchronously.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Command'
              example:
                command_id: cmd_610
                battery_id: bat_1
                action: discharge
                parameters:
                  min_wh: 1500
                  max_wh: null
                start: '2026-11-12T18:00:00Z'
                end: '2026-11-12T19:00:00Z'
                status: pending
                status_reason: null
                batch_id: null
                created_at: '2026-11-12T16:02:51Z'
                updated_at: '2026-11-12T16:02:51Z'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'
        '409':
          $ref: '#/components/responses/Conflict'
        '422':
          $ref: '#/components/responses/Unprocessable'
components:
  parameters:
    BatteryId:
      name: id
      in: path
      required: true
      description: Battery identifier.
      schema:
        type: string
      example: bat_1
    IdempotencyKey:
      name: Idempotency-Key
      in: header
      required: false
      description: >
        Unique value identifying this request (a UUID is recommended). Reuse the
        same

        key and the same body when retrying. See Idempotency in the API
        description.
      schema:
        type: string
        maxLength: 255
      example: 7c1e4a90-5b2d-4f8e-9a31-0d6c2e8b1f47
  schemas:
    DischargeRequest:
      type: object
      allOf:
        - $ref: '#/components/schemas/OptionalWindow'
        - $ref: '#/components/schemas/DischargeParams'
      unevaluatedProperties: false
    Command:
      type: object
      required:
        - command_id
        - battery_id
        - action
        - parameters
        - start
        - end
        - status
        - status_reason
        - batch_id
        - created_at
        - updated_at
      properties:
        command_id:
          type: string
          description: Command identifier.
        battery_id:
          type: string
          description: Battery identifier.
        action:
          $ref: '#/components/schemas/Action'
        parameters:
          $ref: '#/components/schemas/CommandParameters'
        start:
          $ref: '#/components/schemas/Timestamp'
          description: >
            Effective start (inclusive). If the request omitted `start`, or gave
            a time

            in the past, this is the time Kapa accepted the command.
        end:
          $ref: '#/components/schemas/NullableTimestamp'
          description: >-
            End (exclusive). Null means open-ended, until cancelled or
            superseded.
        status:
          $ref: '#/components/schemas/CommandStatus'
        status_reason:
          type:
            - string
            - 'null'
          description: >
            Machine-readable reason when `status` is `failed`, e.g.
            `oem_rejected`,

            `oem_unreachable`, `battery_offline`. Null otherwise.
        batch_id:
          type:
            - string
            - 'null'
          description: >-
            Identifier of the batch that created the command. Null for single
            commands.
        created_at:
          $ref: '#/components/schemas/Timestamp'
          description: When Kapa accepted the command.
        updated_at:
          $ref: '#/components/schemas/Timestamp'
          description: Last status change.
    OptionalWindow:
      type: object
      properties:
        start:
          $ref: '#/components/schemas/NullableTimestamp'
          description: >-
            Start of the command (inclusive). Omitted or null means as soon as
            possible.
        end:
          $ref: '#/components/schemas/NullableTimestamp'
          description: >-
            End of the command (exclusive). Omitted or null means until
            cancelled or superseded.
    DischargeParams:
      type: object
      properties:
        min_wh:
          type:
            - integer
            - 'null'
          minimum: 0
          description: >
            Stored energy level, in Wh, at which discharging stops. Omitted or
            null means

            down to the lowest level the manufacturer allows.
        max_wh:
          type:
            - integer
            - 'null'
          minimum: 0
          description: >-
            **To be confirmed.** The meaning of this parameter for a discharge
            is not defined yet.
    Action:
      type: string
      enum:
        - charge
        - discharge
        - autoconsume
        - idle
      description: |
        - `charge`: charge the battery.
        - `discharge`: discharge the battery.
        - `autoconsume`: self-consumption.
        - `idle`: neither charge nor discharge.
    CommandParameters:
      type: object
      description: >
        Action-specific parameters, as accepted. `charge` uses `allow_import`
        and

        `max_wh`; `discharge` and `autoconsume` use `min_wh` and `max_wh`;
        `idle` has none.
      properties:
        allow_import:
          type: boolean
        min_wh:
          type:
            - integer
            - 'null'
          minimum: 0
        max_wh:
          type:
            - integer
            - 'null'
          minimum: 0
      additionalProperties: false
    Timestamp:
      type: string
      format: date-time
      pattern: ^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(\.\d{1,9})?Z$
      description: RFC 3339 timestamp in UTC, with a `Z` suffix.
      examples:
        - '2026-11-12T18:00:00Z'
    NullableTimestamp:
      type:
        - string
        - 'null'
      format: date-time
      pattern: ^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(\.\d{1,9})?Z$
      description: RFC 3339 timestamp in UTC, with a `Z` suffix, or null.
    CommandStatus:
      type: string
      enum:
        - pending
        - scheduled
        - active
        - completed
        - cancelled
        - superseded
        - failed
      description: >
        - `pending`: accepted by Kapa, not yet acknowledged by the
        manufacturer's cloud.

        - `scheduled`: acknowledged by the manufacturer; `start` is in the
        future.

        - `active`: in effect on the battery.

        - `completed`: the window ended normally.

        - `cancelled`: cancelled with `cancel_command`.

        - `superseded`: the whole window is covered by more recent commands.

        - `failed`: the command could not be applied; see `status_reason`.
    Problem:
      type: object
      description: Error details, following RFC 9457 (Problem Details for HTTP APIs).
      required:
        - type
        - title
        - status
        - code
      properties:
        type:
          type: string
          format: uri-reference
          description: URI identifying the problem type.
        title:
          type: string
          description: Short, human-readable summary of the problem type.
        status:
          type: integer
          description: HTTP status code.
        code:
          type: string
          description: >-
            Stable, machine-readable error code. See Errors in the API
            description.
        detail:
          type: string
          description: Human-readable explanation specific to this occurrence.
        invalid_params:
          type: array
          description: Parameters that caused the error, if any.
          items:
            type: object
            required:
              - name
              - reason
            properties:
              name:
                type: string
              reason:
                type: string
  responses:
    BadRequest:
      description: The request is malformed or its parameters are invalid.
      content:
        application/problem+json:
          schema:
            $ref: '#/components/schemas/Problem'
          example:
            type: https://api.kapa.energy/problems/invalid-time-window
            title: Invalid time window
            status: 400
            code: invalid_time_window
            detail: end must be after start.
            invalid_params:
              - name: end
                reason: must be after start
    Unauthorized:
      description: The access token is missing, invalid or expired.
      content:
        application/problem+json:
          schema:
            $ref: '#/components/schemas/Problem'
          example:
            type: https://api.kapa.energy/problems/unauthorized
            title: Unauthorized
            status: 401
            code: unauthorized
            detail: The access token has expired.
    NotFound:
      description: No battery with this id is accessible to the client.
      content:
        application/problem+json:
          schema:
            $ref: '#/components/schemas/Problem'
          example:
            type: https://api.kapa.energy/problems/battery-not-found
            title: Battery not found
            status: 404
            code: battery_not_found
            detail: No battery with id bat_9 is accessible to this client.
    Conflict:
      description: A request with the same Idempotency-Key is still being processed.
      content:
        application/problem+json:
          schema:
            $ref: '#/components/schemas/Problem'
          example:
            type: https://api.kapa.energy/problems/idempotency-key-in-use
            title: Idempotency key in use
            status: 409
            code: idempotency_key_in_use
            detail: Retry once the original request has completed.
    Unprocessable:
      description: >
        The request is valid but cannot be applied to this battery

        (`action_not_supported`, `energy_out_of_range`), or the Idempotency-Key
        was

        already used with a different body (`idempotency_key_reused`).
      content:
        application/problem+json:
          schema:
            $ref: '#/components/schemas/Problem'
          example:
            type: https://api.kapa.energy/problems/energy-out-of-range
            title: Energy out of range
            status: 422
            code: energy_out_of_range
            detail: >-
              max_wh (15000) exceeds the usable capacity of this battery (13000
              Wh).
            invalid_params:
              - name: max_wh
                reason: exceeds usable_capacity_wh
  securitySchemes:
    oauth2:
      type: oauth2
      description: OAuth 2.0 client credentials. Obtain a token from `POST /token`.
      flows:
        clientCredentials:
          tokenUrl: https://api.kapa.energy/token
          scopes: {}

````

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