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

# Send a batch of commands

> Sends up to 1,000 commands, to one or many batteries, in a single request. Each
operation takes the same parameters as the matching single-battery endpoint,
plus `id` (the battery) and `action`. In a batch, `start` and `end` are required
for every operation. A battery can appear in several operations if their
windows do not overlap.

Kapa validates every operation immediately and returns `202 Accepted` with one
result per operation. Each accepted operation becomes a command carrying the
batch's `batch_id`, delivered to the manufacturer asynchronously. Follow it like
any other command.

- **Whole-batch errors (400):** invalid JSON, schema violations, more than 1,000
  operations, or two operations targeting the same battery over overlapping
  windows.
- **Per-operation errors:** unknown battery, command not applicable to that
  battery, invalid time window. Only that operation is rejected; the others
  proceed.

Batches are not atomic: there is no rollback if some commands fail later.




## OpenAPI

````yaml /openapi.yaml post /bat/v1/batches
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/batches:
    post:
      tags:
        - Batches
      summary: Send a batch of commands
      description: >
        Sends up to 1,000 commands, to one or many batteries, in a single
        request. Each

        operation takes the same parameters as the matching single-battery
        endpoint,

        plus `id` (the battery) and `action`. In a batch, `start` and `end` are
        required

        for every operation. A battery can appear in several operations if their

        windows do not overlap.


        Kapa validates every operation immediately and returns `202 Accepted`
        with one

        result per operation. Each accepted operation becomes a command carrying
        the

        batch's `batch_id`, delivered to the manufacturer asynchronously. Follow
        it like

        any other command.


        - **Whole-batch errors (400):** invalid JSON, schema violations, more
        than 1,000
          operations, or two operations targeting the same battery over overlapping
          windows.
        - **Per-operation errors:** unknown battery, command not applicable to
        that
          battery, invalid time window. Only that operation is rejected; the others
          proceed.

        Batches are not atomic: there is no rollback if some commands fail
        later.
      operationId: createBatch
      parameters:
        - $ref: '#/components/parameters/IdempotencyKey'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/BatchRequest'
            example:
              operations:
                - id: bat_1
                  action: charge
                  start: '2026-11-12T13:00:00Z'
                  end: '2026-11-12T15:00:00Z'
                  allow_import: true
                  max_wh: 12000
                - id: bat_2
                  action: discharge
                  start: '2026-11-12T18:00:00Z'
                  end: '2026-11-12T19:00:00Z'
                  min_wh: 1500
                - id: bat_3
                  action: idle
                  start: '2026-11-12T18:00:00Z'
                  end: '2026-11-12T20:00:00Z'
      responses:
        '202':
          description: Batch validated. One result per operation, in request order.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/BatchResponse'
              example:
                batch_id: bch_81
                summary:
                  total: 3
                  accepted: 2
                  rejected: 1
                results:
                  - index: 0
                    id: bat_1
                    status: accepted
                    command_id: cmd_701
                    error: null
                  - index: 1
                    id: bat_2
                    status: accepted
                    command_id: cmd_702
                    error: null
                  - index: 2
                    id: bat_3
                    status: rejected
                    command_id: null
                    error:
                      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_3 is accessible to this client.
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '409':
          $ref: '#/components/responses/Conflict'
        '422':
          $ref: '#/components/responses/Unprocessable'
components:
  parameters:
    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:
    BatchRequest:
      type: object
      required:
        - operations
      properties:
        operations:
          type: array
          minItems: 1
          maxItems: 1000
          items:
            $ref: '#/components/schemas/BatchOperation'
      additionalProperties: false
    BatchResponse:
      type: object
      required:
        - batch_id
        - summary
        - results
      properties:
        batch_id:
          type: string
          description: Batch identifier, also set on every command the batch created.
        summary:
          type: object
          required:
            - total
            - accepted
            - rejected
          properties:
            total:
              type: integer
              minimum: 0
            accepted:
              type: integer
              minimum: 0
            rejected:
              type: integer
              minimum: 0
        results:
          type: array
          description: One result per operation, in request order.
          items:
            $ref: '#/components/schemas/BatchOperationResult'
    BatchOperation:
      oneOf:
        - $ref: '#/components/schemas/BatchChargeOperation'
        - $ref: '#/components/schemas/BatchDischargeOperation'
        - $ref: '#/components/schemas/BatchAutoconsumeOperation'
        - $ref: '#/components/schemas/BatchIdleOperation'
      discriminator:
        propertyName: action
        mapping:
          charge: '#/components/schemas/BatchChargeOperation'
          discharge: '#/components/schemas/BatchDischargeOperation'
          autoconsume: '#/components/schemas/BatchAutoconsumeOperation'
          idle: '#/components/schemas/BatchIdleOperation'
    BatchOperationResult:
      type: object
      required:
        - index
        - id
        - status
        - command_id
        - error
      properties:
        index:
          type: integer
          minimum: 0
          description: Position of the operation in the request.
        id:
          type: string
          description: Battery identifier.
        status:
          type: string
          enum:
            - accepted
            - rejected
          description: >
            `accepted` means the operation passed validation and became a
            command; it

            does not mean the command was applied yet.
        command_id:
          type:
            - string
            - 'null'
          description: Identifier of the created command. Null if rejected.
        error:
          description: Reason for rejection. Null if accepted.
          oneOf:
            - $ref: '#/components/schemas/Problem'
            - type: 'null'
    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
    BatchChargeOperation:
      type: object
      required:
        - id
        - action
      properties:
        id:
          type: string
          description: Battery identifier.
        action:
          type: string
          const: charge
      allOf:
        - $ref: '#/components/schemas/RequiredWindow'
        - $ref: '#/components/schemas/ChargeParams'
      unevaluatedProperties: false
    BatchDischargeOperation:
      type: object
      required:
        - id
        - action
      properties:
        id:
          type: string
          description: Battery identifier.
        action:
          type: string
          const: discharge
      allOf:
        - $ref: '#/components/schemas/RequiredWindow'
        - $ref: '#/components/schemas/DischargeParams'
      unevaluatedProperties: false
    BatchAutoconsumeOperation:
      type: object
      required:
        - id
        - action
      properties:
        id:
          type: string
          description: Battery identifier.
        action:
          type: string
          const: autoconsume
      allOf:
        - $ref: '#/components/schemas/RequiredWindow'
        - $ref: '#/components/schemas/AutoconsumeParams'
      unevaluatedProperties: false
    BatchIdleOperation:
      type: object
      required:
        - id
        - action
      properties:
        id:
          type: string
          description: Battery identifier.
        action:
          type: string
          const: idle
      allOf:
        - $ref: '#/components/schemas/RequiredWindow'
      unevaluatedProperties: false
    RequiredWindow:
      type: object
      required:
        - start
        - end
      properties:
        start:
          $ref: '#/components/schemas/Timestamp'
          description: Start of the command (inclusive).
        end:
          $ref: '#/components/schemas/Timestamp'
          description: >-
            End of the command (exclusive). Must be after `start` and in the
            future.
    ChargeParams:
      type: object
      properties:
        allow_import:
          type: boolean
          default: true
          description: >
            If true, the battery may charge from the grid. If false, it only
            charges from

            on-site production (PV surplus).
        max_wh:
          type:
            - integer
            - 'null'
          minimum: 0
          description: >
            Stored energy level, in Wh, at which charging stops. Must not exceed

            `usable_capacity_wh`. Omitted or null means up to the full usable
            capacity.
    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.
    AutoconsumeParams:
      type: object
      properties:
        min_wh:
          type:
            - integer
            - 'null'
          minimum: 0
          description: >
            Lowest stored energy level, in Wh. Below it, the battery stops
            discharging to

            cover site consumption. Omitted or null means the lowest level the

            manufacturer allows.
        max_wh:
          type:
            - integer
            - 'null'
          minimum: 0
          description: >
            Highest stored energy level, in Wh. Above it, the battery stops
            charging from

            on-site production. Must be greater than or equal to `min_wh` and
            must not

            exceed `usable_capacity_wh`. Omitted or null means the full usable
            capacity.
    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'
  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.
    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.