Schemas and examples

Admin API schemas and examples

This file defines the reusable JSON types named by Endpoint reference. ? means optional on input; | null means the property is present and nullable on output unless the surrounding endpoint says otherwise. DateTime is an RFC 3339 string; request date-times must include an offset.

Primitive and lookup types

PositiveInt = integer > 0
NonnegativeInt = integer >= 0
DateTime = RFC3339 string, for example "2026-08-24T12:30:00Z"
NamedLookup = { id: PositiveInt, name: string }
SiteRef = { id: PositiveInt, name: string|null }
Cost = { currency: string, net: number, tax: number, total: number }

Unknown future fields should be ignored. IDs are JSON numbers except opaque cursors and access-token IDs managed by the portal. The API does not return raw RFID UIDs, charger passwords, workspace tokens, contractId, provider secrets, or unredacted secret-like configuration values.

Request coercion and unknown properties

The API does not apply one global numeric conversion policy:

  • Every resource path ID and numeric query parameter accepts a decimal string such as "12" when it satisfies the documented integer/range constraint.
  • Workspace countryId; site countryId/timezoneId; charger site/connector and command numeric fields; every numeric tariff field; and every numeric charging-profile field are also coerced from numeric JSON strings.
  • Site latitude/longitude, charger smartMode, all power-management numbers, report-schedule numbers, and user/RFID/access/limit/local-user body IDs and values require JSON numbers and are not coerced.
  • Tariff, charging-profile, report-schedule, and user body objects reject unknown properties with 422. Workspace, site, power-management, other charger/report objects, and query objects ignore unknown properties.

These details are transport behavior, not a recommendation: clients should send JSON numbers for numeric body fields.

Workspace and site types

Workspace = {
  id: PositiveInt,
  name: string,
  email: string|null,
  website: string|null,
  address: {
    line1: string|null, line2: string|null, postalCode: string|null,
    city: string|null, state: string|null, countryId: PositiveInt,
    countryCode: string, countryName: string
  },
  createdAt: DateTime,
  updatedAt: DateTime
}

Site = {
  id: PositiveInt,
  name: string,
  description: string|null,
  address: {
    line1: string|null, line2: string|null, postalCode: string|null,
    city: string|null, state: string|null, countryId: PositiveInt,
    countryCode: string, countryName: string
  },
  timezone: { id: PositiveInt, name: string },
  latitude: number|null,
  longitude: number|null,
  chargerCount: integer
}

SiteSession = {
  id: PositiveInt,
  charger: { id: PositiveInt, name: string },
  portId: PositiveInt,
  connectorId: integer,
  active: boolean,
  startedAt: DateTime,
  stoppedAt: DateTime|null,
  durationSeconds: integer|null,
  energyWh: number,
  costs: Cost[]
}

MetricsWindow = {
  from: DateTime, to: DateTime, sessionLimit: integer,
  sessionsIncluded: integer, totalsCapped: boolean
}

GET /sites/:siteId returns { site, recentSessions, totals, metricsWindow }. Its site adds owned charger/connector state to the base site projection; totals contains sessions, energyWh, energyKwh, durationSeconds, and costs: Cost[].

Site lookups are { countries: {countryId, countryCode, name}[], timezones: {timezoneId, name}[] } with numeric IDs and string names/codes.

DashboardSession = {
  id: PositiveInt, chargerId: PositiveInt, connectorId: PositiveInt,
  userId: PositiveInt|null, userName: string|null, chargerName: string|null,
  site: SiteRef|null, status: string|null, energyWh: number,
  startedAt: DateTime|null, stoppedAt: DateTime|null,
  costs: Array<{ currency: string|null, price: number, tax: number }>
}

Dashboard = {
  window: { from: DateTime, to: DateTime, maxSessions: 1000 },
  stats: { chargerCount: integer, onlineChargerCount: integer, siteCount: integer, userCount: integer },
  sessions: DashboardSession[],
  analytics: {
    totals: { sessions: integer, energyWh: number, durationSeconds: integer, costs: Array<{currency:string, amount:number}> },
    daily: Array<{ date:string, sessions:integer, energyWh:number, costs:Array<{currency:string, amount:number}> }>,
    sites: Array<{ id:PositiveInt, name:string|null, sessions:integer, energyWh:number, costs:Array<{currency:string, amount:number}> }>,
    chargers: Array<{ id:PositiveInt, name:string|null, siteId:PositiveInt|null, siteName:string|null, sessions:integer, energyWh:number, costs:Array<{currency:string, amount:number}> }>
  }
}

SiteDetailCharger = {
  id: PositiveInt, name: string|null, identifier: string|null, status: string,
  connection: { lastSeenAt: DateTime|null, leaseExpiresAt: DateTime|null }|null,
  connectors: Array<{ evseId:PositiveInt, evseNumber:integer, portId:PositiveInt, connectorId:integer, status:string|null }>,
  sessionCount: integer,
  totals: { sessions:integer, energyWh:number, energyKwh:number, durationSeconds:integer, costs:Cost[] }
}

Example workspace patch and response:

PATCH /v2/admin
Authorization: Bearer evc_ws_<selector>.<secret>
Idempotency-Key: workspace-contact-20260824
Content-Type: application/json

{"email":"ops@example.com","city":"Riga"}
{
  "workspace": {
    "id": 7,
    "name": "Example CPO",
    "email": "ops@example.com",
    "website": null,
    "address": {
      "line1": "1 Example Street",
      "line2": null,
      "postalCode": "LV-1001",
      "city": "Riga",
      "state": null,
      "countryId": 1,
      "countryCode": "LV",
      "countryName": "Latvia"
    },
    "createdAt": "2026-01-01T00:00:00.000Z",
    "updatedAt": "2026-08-24T12:30:00.000Z"
  }
}

Site create/update example (fields with defaults may be omitted):

{
  "name": "Main depot",
  "addressLine1": "1 Example Street",
  "city": "Riga",
  "countryId": 1,
  "timezoneId": 1,
  "latitude": 56.9496,
  "longitude": 24.1052
}

Power-management response

PowerConfiguration = {
  site: { id: PositiveInt, updatedAt: DateTime },
  chargepoints: Array<{
    id: PositiveInt, name: string|null,
    compatible: boolean, compatibility: string, // values come from shared capability projection
    ...additional smart-charging capability flags
  }>,
  pools: Array<{
    id: PositiveInt, parentId: PositiveInt|null, name: string|null,
    limit: integer, defaultLimit: integer|null, mode: integer|string|null
  }>,
  assignments: Array<{
    poolId: PositiveInt, chargepointId: PositiveInt, priority: integer,
    chargingProfileId: PositiveInt|null,
    chargingProfileSynced: integer|null,
    limit: number|null
  }>,
  meterSources: Array<{
    id: PositiveInt, poolId: PositiveInt, sourceId: PositiveInt,
    provider: string, name: string|null, externalId: string,
    measurementMode: string, valueMode: string,
    reserveA: integer, offlineSafeA: integer, ttlSeconds: integer,
    enabled: boolean, lastComputedLimitA: number|null,
    lastComputedAt: DateTime|null, lastStatus: string|null,
    lastSeenAt: DateTime|null, effectiveLimitA: number|null
  }>,
  connectors: Array<{
    id: PositiveInt, connectorId: integer, chargepointId: PositiveInt,
    status: string|null
  }>
}

The exact names of the additional compatibility fields are produced by smartChargingCompatibility(); that helper's return type is not explicitly named in the source. This is the only response fragment whose closed field list cannot be determined from an exported type, so additional fields are deliberately marked unclear rather than invented.

Replacement example:

{
  "pools": [
    {
      "name": "Depot supply",
      "limit": 125,
      "meterSources": [
        {
          "externalId": "meter-42",
          "measurementMode": "available_headroom",
          "enabled": true
        }
      ],
      "children": [
        {"chargepointId": 11, "priority": 10},
        {"name": "Secondary", "limit": 32, "children": []}
      ]
    }
  ]
}

Charger types

Charger = {
  id: PositiveInt, name: string, identifier: string, cpoId: PositiveInt,
  site: { id: PositiveInt, name: string }, status: string,
  isPublic: boolean, invertedRfid: boolean, smartMode: integer,
  automationDisabled: boolean, provisioned: boolean,
  connection: {
    protocol: string|null, lastSeenAt: DateTime|null, leaseExpiresAt: DateTime|null
  } | null,
  evses: Array<{ id: PositiveInt, number: integer, status: string|null }>,
  connectors: Array<{
    evseId: PositiveInt, evseNumber: integer, status: string|null,
    id: PositiveInt, connectorId: integer, portNumber: integer,
    format: { id: PositiveInt, name: string, description: string|null },
    type: { id: PositiveInt, name: string, description: string|null },
    powerType: { id: PositiveInt, name: string, description: string|null },
    maxVoltage: number, maxAmperage: number, maxElectricPower: number,
    maxPowerKw: number
  }>,
  createdAt: DateTime, updatedAt: DateTime
}

ChargerDetail = Charger & {
  configuration: Array<{
    chargepointConfigId: PositiveInt, key: string,
    value: string|null, readonly: boolean|number, setByServer: boolean|number,
    modified: DateTime
  }>,
  firmware: { status: string|null, statusAt: DateTime|null, version: string|null, bootAt: DateTime|null },
  counts: { localUsers: integer, sessions: integer, tariffAssignments: integer, profileAssignments: integer }
}

The list projection adds bounded metric fields and returns metricsWindow; the detail projection uses the shape above. Configuration values for sensitive keys are exactly "[REDACTED]".

PublicReadiness = {
  ready: boolean,
  missingRequirements: Array<"site_coordinates"|"connector_tariffs"|"billing_plan"|"stripe_connect"|"legacy_payment_data">,
  commercialGateEnabled: boolean, siteCoordinatesDefined: boolean,
  allConnectorsHaveTariffs: boolean, connectorsMissingTariffs: integer[],
  connectorCount: integer, paymentDataDefined: boolean,
  billingAccountId: PositiveInt|null, planCode: string|null,
  billingPlanEligible: boolean, stripeConnectReady: boolean,
  stripeConnectState: "active"|"action_required"|"pending"|"not_configured",
  connectorIds: integer[], missingTariffConnectorIds: integer[]
}

RedactedOcppLog = {
  ocppServerLogId: PositiveInt, timestamp: DateTime, logLevelId: PositiveInt,
  logAction: string|null, logDirection: string|null, logMessage: string|null,
  messageId: string|null, transactionLogId: PositiveInt|null,
  logLevel: { name:string }|null
}

CompositeSchedule = {
  id: PositiveInt, chargepointId: PositiveInt, connectorId: integer,
  evseId: PositiveInt|null, scheduleStart: DateTime, duration: integer,
  minChargingRate: number|null, chargingRateUnitId: PositiveInt,
  chargingRateUnit: string|null, chargingRateUnitDescription: string|null,
  periods: Array<{ id:PositiveInt, startPeriod:number, limit:number, numberPhases:integer|null, phaseToUse:integer|null }>
}

Connector patch success returns exactly { portId, connectorId, ocpiConnectorFormatEnumId, ocpiConnectorTypeEnumId, ocpiPowerTypeEnumId, maxVoltage, maxAmperage, maxElectricPower }.

connectorLookups contains these lookup shapes: formats use { ocpiConnectorFormatEnumId, name, description }; types use { ocpiConnectorTypeEnumId, name, description, icon }; power types use { ocpiPowerTypeEnumId, name, shortDescr, description, numPhases }.

Create and connector-update examples:

{"name":"Warehouse 1","identifier":"cp-warehouse-1","password":"initial-secret","siteId":3}
{
  "connectorFormatId": 1,
  "connectorTypeId": 2,
  "powerTypeId": 1,
  "maxVoltage": 400,
  "maxAmperage": 32,
  "maxElectricPower": 22000
}

Command response objects contain status: string. Trigger additionally contains connectorResults: { connectorId: number, status: string }[]; composite schedule additionally contains connectorId and compositeSchedule (or null). The status values are persisted OCPP/downstream results and are not narrowed to a closed enum by the route.

Report types

ReportSession = {
  id: PositiveInt,
  charger: { id: PositiveInt, name: string, site: { id: PositiveInt, name: string }|null },
  portId: PositiveInt, connectorId: integer,
  user: { id: PositiveInt, name: string }|null,
  active: boolean, energyWh: number,
  startedAt: DateTime, stoppedAt: DateTime|null,
  durationSeconds: integer|null, costs: Cost[]
}

ReportSchedule = {
  id: PositiveInt, workspaceId: PositiveInt, enabled: boolean,
  recipientEmail: string, format: "csv"|"pdf", sendDay: integer,
  sendTime: string, createdAt: DateTime, updatedAt: DateTime,
  lastDeliveryPeriod: string|null, lastDeliveryStatus: string|null,
  lastDeliveryAttemptAt: DateTime|null, lastDeliveryAttemptCount: integer,
  lastDeliveredPeriod: string|null, lastDeliveredAt: DateTime|null
}

ReportDataResponse = {
  items: ReportSession[],
  pageTotals: { sessions:integer, energyWh:number, durationSeconds:integer, costs:Cost[] },
  page: { limit:integer, nextCursor:string|null },
  range: { from:DateTime, to:DateTime, toExclusive:true }
}

MeasurementPoint = { meterValueId:PositiveInt, timestamp:DateTime, elapsedSeconds:integer, value:number }
Measurement = {
  name:string,
  unit:{ symbol:string|null, description:string|null },
  lines:Array<{ phase:string, points:MeasurementPoint[] }>
}
MeasurementsResponse = {
  transactionLogId:PositiveInt, measurements:Measurement[],
  page:{ limit:integer, nextCursor:string|null, sampleValuesTruncated:boolean, sampleLimitPerMeterValue:64 }
}

Schedule request and response example:

{"enabled":true,"recipientEmail":"reports@example.com","format":"csv","sendDay":5,"sendTime":"08:30"}
{
  "schedule": {
    "id": 4,
    "workspaceId": 7,
    "enabled": true,
    "recipientEmail": "reports@example.com",
    "format": "csv",
    "sendDay": 5,
    "sendTime": "08:30",
    "createdAt": "2026-08-01T10:00:00.000Z",
    "updatedAt": "2026-08-24T12:30:00.000Z",
    "lastDeliveryPeriod": null,
    "lastDeliveryStatus": null,
    "lastDeliveryAttemptAt": null,
    "lastDeliveryAttemptCount": 0,
    "lastDeliveredPeriod": null,
    "lastDeliveredAt": null
  }
}

Tariff types

TariffComponent = {
  id: PositiveInt,
  type: NamedLookup, season: NamedLookup,
  validFromMonth: number|null, validToMonth: number|null,
  validFromWeekDay: number|null, validToWeekDay: number|null,
  validFromHour: number|null, validToHour: number|null,
  priority: number, price: number
}

Tariff = {
  id: PositiveInt, cpoId: PositiveInt, name: string,
  type: NamedLookup,
  site: { id: PositiveInt, name: string, timezone: string|null }|null,
  currency: { id: PositiveInt, code: string, sign: string },
  biddingZone: { id: PositiveInt, code: string, name: string }|null,
  tax: number, fallbackPrice: number|null, price: number|null,
  components: TariffComponent[], assignmentCount: integer
}

TariffAssignment = {
  id: PositiveInt, chargerId: PositiveInt, connectorId: integer,
  tariff: { id: PositiveInt, name: string, currency: { code: string, sign: string } },
  createdAt: DateTime, updatedAt: DateTime
}

MarketPrice = {
  id: PositiveInt, value: number, currencyId: PositiveInt,
  startsAt: integer, endsAt: integer,
  startsAtIso: DateTime, endsAtIso: DateTime, intervalMinutes: number
}

TariffLookups = {
  currencies:Array<{id:PositiveInt,code:string,sign:string,fractionalUnit:string,enabled:boolean,minimalValue:integer}>,
  biddingZones:Array<{id:PositiveInt,code:string,name:string,currencyId:PositiveInt,enabled:boolean}>,
  currencyRates:Array<{currencyId:PositiveInt,rate:number,updatedAt:DateTime}>,
  componentTypes:NamedLookup[], seasons:NamedLookup[], tariffTypes:NamedLookup[]
}

Tariff and component examples:

{"name":"Public AC","currencyCode":"EUR","biddingZoneId":1,"tax":21}
{
  "components": [
    {"typeId":1,"seasonId":1,"priority":0,"price":0.25,"validFromHour":0,"validToHour":24}
  ]
}

Charging-profile types

SchedulePeriod = { id: PositiveInt, startPeriod: number, limit: number, numberOfPhases: integer|null }

ChargingProfile = {
  id: PositiveInt, cpoId: PositiveInt, label: string, description: string|null,
  stackLevel: number, location: NamedLookup, purpose: NamedLookup,
  kind: NamedLookup, recurrency: NamedLookup|null,
  validFrom: DateTime|null, validTo: DateTime|null,
  startSchedule: DateTime|null, durationSeconds: number|null,
  rateUnit: NamedLookup, minChargingRate: number|null,
  schedule: SchedulePeriod[], assignmentCount: integer,
  createdAt: DateTime, updatedAt: DateTime
}

ProfileAssignment = {
  id: PositiveInt, chargerId: PositiveInt, connectorId: integer,
  synced: boolean, syncCode: integer,
  syncStatus: "accepted"|"rejected"|"unknown",
  profile: { id: PositiveInt, label: string, stackLevel: number, purpose: string, kind: string|null },
  createdAt: DateTime, updatedAt: DateTime
}

ChargingProfileLookups = {
  locations:NamedLookup[], purposes:NamedLookup[], kinds:NamedLookup[],
  recurrencyKinds:NamedLookup[],
  rateUnits:Array<{id:PositiveInt,name:string,supported:boolean}>,
  allowedRateUnits:string[]
}

Create and schedule examples:

{
  "label":"Depot cap","locationId":3,"stackLevel":1,"purposeId":1,"kindId":1,
  "recurrencyKindId":null,"validFrom":null,"validTo":null,"startSchedule":null,
  "rateUnitId":1,"minChargingRate":6,
  "schedule":[{"startPeriod":0,"limit":32,"numberOfPhases":3}]
}
{"rateUnitId":1,"periods":[{"startPeriod":0,"limit":16,"numberOfPhases":3}]}

User types

RFID = { id: PositiveInt, maskedUid: string, name: string|null, type: string, valid: boolean }
AccessItem = { id: PositiveInt, chargerId: PositiveInt, chargerName: string, expiresAt: DateTime|null, site: SiteRef|null }
Access = { allChargers: boolean, chargerIds: PositiveInt[], items: AccessItem[] }
Limit = { id: PositiveInt, limitTypeId: PositiveInt, label: string, energyValue: integer, timeValue: integer }

User = {
  id: PositiveInt, membershipId: PositiveInt, name: string, email: string,
  rfids: RFID[], access: AccessItem[], limitCount: integer
}

UserDetail = User & {
  phone: string|null, createdAt: DateTime|null, limits: Limit[]
}

Vehicle = {
  id: PositiveInt, name: string|null, provider: string|null, status: string|null,
  vin: string|null, identifier: string|null, model: string|null,
  batteryCapacityWh: number|null, createdAt: DateTime,
  telemetry: { soc: number|null, chargeLimitSoc: number|null, recordedAt: DateTime|null }|null
}

LocalUser = {
  id: PositiveInt, customerId: PositiveInt, name: string, email: string,
  expiresAt: DateTime|null, systemManaged: boolean
}

LocalUserMutation = {
  chargepointLocalUserId: PositiveInt,
  customerId: PositiveInt,
  expiryDate: DateTime|null,
  customer: {} // `contractId` is removed by the workspace-admin response boundary
}
LocalUserUpdate = { id:PositiveInt, customerId:PositiveInt, expiresAt:DateTime|null }

Invitation = {
  email:string, name:string, chargerIds:PositiveInt[], rfids:string[]
}

UserListResponse = {
  items:User[], invitations:Invitation[], chargers:Array<{id:PositiveInt,name:string,identifier:string|null,site:SiteRef|null}>,
  page:{ nextCursor:number|string|null, memberNextCursor:number|null, invitationNextCursor:string|null, limit:integer }
}

UserSession = {
  id:PositiveInt, chargerId:PositiveInt, chargerName:string|null,
  connectorId:PositiveInt, site:SiteRef|null, energyWh:number,
  startedAt:DateTime|null, stoppedAt:DateTime|null
}

The mutation response for POST /chargers/:chargerId/local-users and the existing-customer result of POST /chargers/:chargerId/local-users/invitations return LocalUserMutation; PATCH /chargers/:chargerId/local-users/:localUserId returns LocalUserUpdate. The list endpoint uses LocalUser. Clients should treat these as intentionally distinct response types.

User/invitation/RFID/access examples:

{"email":"driver@example.com","name":"Example Driver"}
{
  "email":"new.driver@example.com",
  "name":"New Driver",
  "rfid":"04AABBCCDD",
  "allChargers":false,
  "chargerIds":[11,12]
}
{"uid":"04AABBCCDD","name":"Blue card"}
{"allChargers":false,"chargerIds":[11,12]}

Successful writes never echo the raw RFID UID. For example:

{"rfid":{"id":31,"maskedUid":"******CCDD","name":"Blue card","type":"RFID","valid":true}}

Empty and binary success bodies

  • 204 means no response body.
  • Charger QR success is PNG bytes, not JSON.
  • Report export success is CSV or PDF bytes, not JSON.
  • See Response and error contract for the JSON bodies returned when either binary endpoint fails.

Remaining mutation payload examples

The paths in this example-only table are relative to /v2/admin; the full paths are in Endpoint reference. The longer examples above cover workspace, site, power-management, charger creation/connector settings, report schedules, tariffs/components, charging profiles/schedules, users/invitations, RFID, and access replacement. Every other body-bearing mutation uses one of these exact example shapes:

EndpointExample JSON body
PATCH /chargers/:chargerId{"name":"Warehouse east","siteId":4,"invertedRfid":false,"smartMode":1,"automationDisabled":false}; any non-empty subset is accepted
PATCH /chargers/:chargerId/credentials{"password":"replacement-secret"}
PATCH /chargers/:chargerId/public-listing{"isPublic":true}
PATCH /chargers/:chargerId/configuration{"key":"HeartbeatInterval","value":"60"}
POST /chargers/:chargerId/firmware{"url":"https://firmware.example.com/charger-v2.bin"}
POST /chargers/:chargerId/commands/reset{"resetType":"Soft"}
POST /chargers/:chargerId/commands/availability{"availabilityType":"Operative"}
POST /chargers/:chargerId/commands/trigger{"messageType":"StatusNotification"}
POST /chargers/:chargerId/commands/unlock{"connectorId":1}
POST /chargers/:chargerId/commands/composite-schedule{"connectorId":0,"durationSeconds":86400,"chargingRateUnit":"W"}
POST /chargers/:chargerId/tariff-assignments{"tariffId":17,"connectorId":0}
PATCH /chargers/:chargerId/tariff-assignments/:assignmentId{"tariffId":17,"connectorId":1}
POST or PATCH /chargers/:chargerId/profile-assignments[...]{"profileId":8,"connectorId":1}
PATCH /users/:userId{"name":"Updated Driver"}
POST /users/invitations/resend or DELETE /users/invitations{"email":"new.driver@example.com"}
PATCH /users/:userId/rfids/:rfidId{"name":"Replacement label"}; null clears the label
POST /users/:userId/limits{"limitTypeId":1,"energyValue":20000,"timeValue":0}
PATCH /users/:userId/limits/:limitId{"energyValue":25000}
POST /chargers/:chargerId/local-users{"customerId":42,"expiresAt":"2026-12-31T23:59:59Z"}
POST /chargers/:chargerId/local-users/invitations or its DELETE counterpart{"email":"driver@example.com"}
PATCH /chargers/:chargerId/local-users/:localUserId{"expiresAt":null}

All other mutations have no request body. They still require exactly one Idempotency-Key header.

Additional success examples

Empty lists are valid and make the top-level envelopes unambiguous:

{"items":[],"metricsWindow":{"from":"2026-05-26T00:00:00.000Z","to":"2026-08-24T00:00:00.000Z","sessionLimit":10000,"sessionsIncluded":0,"totalsCapped":false},"connectorLookups":{"formats":[],"types":[],"powerTypes":[]}}
{"items":[],"pageTotals":{"sessions":0,"energyWh":0,"durationSeconds":0,"costs":[]},"page":{"limit":50,"nextCursor":null},"range":{"from":"2026-07-01T00:00:00.000Z","to":"2026-08-01T00:00:00.000Z","toExclusive":true}}
{"status":"Accepted"}
{"status":"Accepted","connectorResults":[{"connectorId":1,"status":"Accepted"}]}
{"sent":true,"format":"csv","rowCount":12,"range":{"from":"2026-07-01T00:00:00.000Z","to":"2026-08-01T00:00:00.000Z"}}
{"items":[],"invitations":[],"chargers":[],"page":{"nextCursor":null,"memberNextCursor":null,"invitationNextCursor":null,"limit":50}}

For object-bearing responses, substitute the exact named DTO from the sections above into the envelope listed in Endpoint reference; for example, tariff creation is { "tariff": Tariff }, profile creation is { "profile": ChargingProfile }, and user creation is { "user": User }.