This is a development version of the documentation. Content may change without notice.
Voke Documentation
ESM Partners

VCP data model

Envelope structure, payload schemas, enums, and identifier conventions for every VCP message on the vcp exchange.

Overview

VCP (Voke Communication Protocol) is the message contract between a partner ESM adapter and Voke. Every message on the vcp exchange — inbound commands, config, and schedules published by the partner, and outbound events published by Voke — uses the same JSON envelope. The payload field carries the concrete data; its shape is determined by the routing key.


The envelope

Every VCP message, regardless of direction or routing key, must conform to VcpMessageEnvelopeSchema:

interface VcpMessage<T> {
  version: '1.1';
  messageId: string;
  correlationId?: string;
  timestamp: string;       // ISO 8601 UTC, validated with z.string().datetime()
  source: string;
  siteId: string;
  payload: T;
}
FieldRequiredDescription
versionYesLiteral '1.1'. Voke rejects envelopes with any other value. Future breaking changes will increment this string.
messageIdYesUnique message identifier. Use UUIDv4. Exists for partner-side bookkeeping and deduplication (see VCP message integrity).
correlationIdNoTies an outbound event back to the inbound command that triggered it. Set this on every command you want to track; Voke echoes it in the resulting event.command.ack and event.execution messages.
timestampYesISO 8601 UTC (2024-05-01T12:00:00.000Z). Voke validates parseability via Zod .datetime(); keep your clock NTP-synced.
sourceYesIdentifies the sender. Voke-originated events set this to 'voke'. Partners should use a stable identifier such as their ESM code (e.g. 'cpi-energo', 'obzor').
siteIdYesThe plant's externalPlantId — the partner-facing identifier the Voke operator sets on the plant. Maps the message to a specific managed site.
payloadYesThe message body. Shape depends on routing key (see below).
signatureAlgoConditionalOptional algorithm marker. Use HMAC-SHA256 when signing.
signatureConditionalBase64url HMAC over the canonicalized envelope excluding signature. Required for command.device, command.mode, and schedule.*.

Inbound payload shapes (partner → Voke)

Command payloads

Published to vcp.{slug}.command, routing key {slug}.command.{subtype}.

SiteSetpointPayload{slug}.command.site-setpoint

FieldTypeRequiredDescription
type'POWER' | 'ENERGY'YesWhether targetValueKw is a power or energy target.
targetValueKwfinite numberConditionalRequired when type = POWER.
targetValueKwhfinite numberConditionalRequired when type = ENERGY.
intervalMinutesnumberConditionalRequired when type = ENERGY; optional for POWER.
direction'IMPORT' | 'EXPORT'YesWhether the target applies to import or export direction.
includeConsumptionbooleanYesWhether local consumption is included in the site-level target calculation.
prioritySchedulePriorityYesNORMAL, HIGH, or EMERGENCY.
validFromISO 8601 UTCYesEarliest activation time.
validUntilISO 8601 UTCNoExpiry time.

BatchDeviceCommandPayload{slug}.command.device

VCP v1.1 accepts device commands as a batch. A single device command should be wrapped as a one-item commands array.

FieldTypeRequiredDescription
commandsDeviceCommand[]Yes1-32 device commands.

DeviceCommand fields:

FieldTypeRequiredDescription
deviceIdstringYesTarget device's externalId (partner-facing).
assetTypeAssetTypeYesAsset class of the target device (see Enum references).
commandDeviceCommandTypeYesThe command to execute (see Enum references).
params.powerKwnumberNoPower level for charge/discharge commands.
params.percentnumberNoPercent reduction for FVE curtailment.
params.respectLimitsbooleanNoWhether to honour configured SOC and power limits.

deviceId in this payload maps to the sub-device's externalId on the Voke plant — it does not use the Voke UUID. Device-level commands are the only inbound payloads that carry a device-scoped identifier; all other inbound payloads address the site as a whole via siteId in the envelope.

EmergencyCommandPayload{slug}.command.emergency

FieldTypeRequiredDescription
type'STOP' | 'HOLD'YesSTOP shuts down all controllable assets; HOLD freezes at current output.
reasonstring (max 500)NoHuman-readable reason for logging.

OperatingModePayload{slug}.command.mode

FieldTypeRequiredDescription
modeOperatingModeYesTarget operating mode (see Enum references).
reasonstring (max 500)NoReason for the mode change.
validUntilISO 8601 UTCNoExpiry time for the override.

Config payloads

Published to vcp.{slug}.config, routing key {slug}.config.{subtype}.

SiteConstraintsPayload{slug}.config.site-constraints

All fields are optional / nullable. Omit to leave a constraint unchanged.

FieldTypeDescription
maxExportKwnumber | nullMaximum grid export power.
maxImportKwnumber | nullMaximum grid import power.
batteryMinSocPercentnumber | nullMinimum allowed battery SOC.
batteryMaxSocPercentnumber | nullMaximum allowed battery SOC.
maxChargeRateKwnumber | nullMaximum battery charge rate.
maxDischargeRateKwnumber | nullMaximum battery discharge rate.
maxFvePowerKwnumber | nullFVE output cap.
rampRateKwPerSecnumber | nullMaximum ramp rate.
fveOnlyChargingbooleanIf true, only FVE surplus may charge the battery.
maxBatteryCyclesPerDaynumber | nullDaily cycle limit.
energyFlowPriorityEnergyFlowPriorityPriority order for energy routing (see Enum references).

FallbackConfigPayload{slug}.config.fallback-config

FieldTypeRequiredDescription
mode'STOP' | 'HOLD' | 'LAST_PLAN'YesBehaviour when the connection to the ESM is lost.
timeoutSecondsfinite numberYesInactivity duration before fallback activates.

SiteTopologyPayload{slug}.config.site-topology

FieldTypeRequiredDescription
assetsSiteAssetDeclaration[]YesArray of declared assets (see below).
hasLocalConsumptionbooleanYesWhether the site has uncontrolled local load.
isLdsbooleanYesWhether the site participates in local distribution support.
pdsConnectionPointIdstringNoPDS connection point reference.

SiteAssetDeclaration fields:

FieldTypeRequiredDescription
deviceIdstringYesAsset's externalId.
assetTypeAssetTypeYesAsset class.
nominalPowerKwfinite numberYesRated power in kW.
nominalCapacityKwhfinite numberNoRated capacity (batteries).

Schedule payloads

Published to vcp.{slug}.schedule, routing key {slug}.schedule.{subtype}.

ScheduleCreatePayload{slug}.schedule.create

FieldTypeRequiredDescription
scheduleIdstringYesPartner-assigned unique ID for idempotent upserts.
startTimeISO 8601 UTCYesSchedule start.
endTimeISO 8601 UTCYesSchedule end.
slotDurationMinutespositive integerNoSlot size. Defaults to 15.
slotsScheduleSlotPayload[]YesOrdered time slots covering the schedule window.
priority'NORMAL' | 'HIGH'YesSchedule execution priority.

ScheduleSlotPayload fields:

FieldTypeRequiredDescription
slotIndexnon-negative integerYesZero-based slot index within the schedule.
setpointSetpointYesSlot target. Same shape as site setpoint's base fields (type, target value, direction, includeConsumption, optional interval).
modeOperatingModeYesOperating mode that should be active for this slot.
controlModeControlModeYesIDLE=0, CHARGE=1, DISCHARGE=2, AUTO=3.
maxChargePowerKwfinite number | nullYesSlot-level charge cap (null = use site constraint).
maxDischargePowerKwfinite number | nullYesSlot-level discharge cap (null = use site constraint).

ScheduleCancelPayload{slug}.schedule.cancel

FieldTypeRequiredDescription
scheduleIdstringYesID of the schedule to cancel.
siteIdstringYesSite the schedule belongs to.
reasonstring (max 500)NoReason for cancellation.

Outbound payload shapes (Voke → partner)

Published by Voke on the vcp exchange; your consumers receive these.

CanonicalTelemetry{slug}.event.telemetry.realtime

Published by publishTelemetry. All power/energy fields are nullable (null = data unavailable).

FieldTypeDescription
gridPowerKwnumber | nullNet grid power (positive = import).
fvePowerKwnumber | nullFVE generation.
batteryPowerKwnumber | nullBattery power (positive = charging).
consumptionPowerKwnumber | nullSite consumption.
socPercentnumber | nullBattery state of charge (%).
availableBatteryEnergyKwhnumber | nullAvailable battery energy in kWh.
batteryTemperatureCelsiusnumber | nullBattery temperature.
currentOperatingModeOperatingModeActive operating mode.
dataQuality'GOOD' | 'INTERPOLATED' | 'STALE' | 'MISSING'Quality indicator for the measurement.
devicesDeviceTelemetry[]Optional per-device breakdown.

DeviceTelemetry fields:

FieldTypeDescription
deviceIdstringDevice's externalId.
assetTypeAssetTypeAsset class.
powerKwfinite numberDevice power output/input.
socPercentnumberBattery SOC (if applicable).
temperatureCelsiusnumberDevice temperature (if applicable).
availableCapacityKwhnumberAvailable energy capacity (if applicable).
regulationPercentnumberRegulation setpoint tracking (if applicable).

MeterReadingPayload{slug}.event.telemetry.meter

Published by publishMeterReading. VCP v1.1 uses 1-minute absolute meter-register snapshots instead of the older 15-minute interval aggregate.

FieldTypeDescription
readingAtISO 8601 UTCClock-aligned minute timestamp.
metersMeterEntry[]One or more meter register snapshots.
dataQuality'GOOD' | 'DEGRADED' | 'ESTIMATED'Quality indicator.

MeterEntry fields:

FieldTypeDescription
meterIdstringMeter sub-device externalId.
role'GRID' | 'FVE' | 'BESS' | 'CONSUMPTION'Meter role.
importRegisterKwhnumberGrid import register.
exportRegisterKwhnumberGrid export register.
productionRegisterKwhnumberFVE production register.
chargeRegisterKwhnumberBESS charge register.
dischargeRegisterKwhnumberBESS discharge register.
consumptionRegisterKwhnumberConsumption register.

AlarmPayload{slug}.event.alarm

Published by publishAlarm. Represents a PLC-originated condition.

FieldTypeRequiredDescription
alarmIdstringYesUnique alarm instance ID.
severityVcpAlarmSeverityYesP1_CRITICAL, P2_MAJOR, P3_MINOR, or P4_INFO.
codeVcpAlarmCodeYesAlarm type code (see Enum references).
messagestringYesHuman-readable description.
deviceIdstringNoDevice externalId if the alarm is device-scoped.
raisedAtISO 8601 UTCYesWhen the alarm was raised.
clearedAtISO 8601 UTCNoSet when the alarm clears (update message).
actualValuestring | numberNoActual measured value for machine-readable alarms.
expectedValuestring | numberNoExpected target or threshold value.
breachedFieldstringNoField that breached a constraint.
tolerancePercentnumberNoTolerance used when evaluating the alarm.
metadatarecordNoArbitrary key–value context.

VCP uses "alarm" for PLC-originated conditions forwarded upstream. "Alert" refers to Voke rule-generated notifications and is unrelated to this payload.

CommandAckPayload{slug}.event.command.ack

Published by publishCommandAck. Confirms or rejects receipt of an inbound command.

FieldTypeRequiredDescription
status'ACCEPTED' | 'REJECTED' | 'PARTIAL' | 'QUEUED'YesOutcome of command receipt.
commandTypestringYesEchoes the command type from the inbound routing key.
messagestringNoHuman-readable explanation (required on rejection).
rejectionCodeRejectionCodeNoStructured rejection reason (see Enum references).
resultsCommandResult[]Required for PARTIALPer-command result array for batch device commands.

CommandStatusPayload{slug}.event.execution

Published by publishCommandStatus. Execution updates after a command has been accepted.

FieldTypeRequiredDescription
commandTypestringYesThe command type being reported on.
status'EXECUTING' | 'COMPLETED' | 'DEVIATED' | 'FAILED'YesCurrent execution state.
actualValueKwnumberNoObserved output (for setpoint commands).
targetValueKwnumberNoThe requested value for comparison.
deviationKwnumberNoDifference between actual and target.
reasonstringNoReason for deviation or failure.

ScheduleAckPayload{slug}.event.schedule.*

Acknowledges a schedule create or cancel operation.

FieldTypeRequiredDescription
scheduleIdstringYesEchoes the scheduleId from the inbound payload.
siteIdstringYesSite the schedule was applied to.
acceptedbooleanYesWhether Voke accepted the schedule.
statusScheduleStatusYesSchedule lifecycle state (see Enum references).
rejectionReasonstring | nullYesHuman-readable reason if accepted = false.
validatedAtISO 8601 UTCYesWhen Voke validated the schedule.

Enum references

AssetType

enum AssetType {
  BESS      = 'BESS',
  FVE       = 'FVE',
  METER     = 'METER',
  HEAT_PUMP = 'HEAT_PUMP',
  EV_CHARGER= 'EV_CHARGER',
  THERMOSTAT= 'THERMOSTAT',
  INVERTER  = 'INVERTER',
  GENERIC   = 'GENERIC',
}

OperatingMode

enum OperatingMode {
  STANDARD           = 'STANDARD',
  ZERO_EXPORT        = 'ZERO_EXPORT',
  MAX_EXPORT         = 'MAX_EXPORT',
  PEAK_SHAVING       = 'PEAK_SHAVING',
  LOCAL_OPTIMIZATION = 'LOCAL_OPTIMIZATION',
  GRID_TARGET        = 'GRID_TARGET',
  LDS_SUPPORT        = 'LDS_SUPPORT',
}

DeviceCommandType

enum DeviceCommandType {
  FVE_PRODUCE_MAX          = 'FVE_PRODUCE_MAX',
  FVE_REDUCE_PERCENT       = 'FVE_REDUCE_PERCENT',
  FVE_REDUCE_POWER         = 'FVE_REDUCE_POWER',
  FVE_STOP                 = 'FVE_STOP',
  BESS_CHARGE              = 'BESS_CHARGE',
  BESS_DISCHARGE           = 'BESS_DISCHARGE',
  BESS_STOP                = 'BESS_STOP',
  BESS_CHARGE_ONLY         = 'BESS_CHARGE_ONLY',
  BESS_DISCHARGE_ONLY      = 'BESS_DISCHARGE_ONLY',
  BESS_CONTINUOUS_CHARGE   = 'BESS_CONTINUOUS_CHARGE',
}

ControlMode

enum ControlMode {
  IDLE      = 0,
  CHARGE    = 1,
  DISCHARGE = 2,
  AUTO      = 3,
}

EnergyFlowPriority

enum EnergyFlowPriority {
  FVE_BESS_GRID              = 'FVE_BESS_GRID',
  FVE_CONSUMPTION_BESS_GRID  = 'FVE_CONSUMPTION_BESS_GRID',
  FVE_CONSUMPTION_GRID_BESS  = 'FVE_CONSUMPTION_GRID_BESS',
}

VcpAlarmSeverity

enum VcpAlarmSeverity {
  P1_CRITICAL = 'P1_CRITICAL',
  P2_MAJOR    = 'P2_MAJOR',
  P3_MINOR    = 'P3_MINOR',
  P4_INFO     = 'P4_INFO',
}

VcpAlarmCode

enum VcpAlarmCode {
  COMM_LOSS                 = 'COMM_LOSS',
  COMM_DEGRADED             = 'COMM_DEGRADED',
  BESS_SOC_LOW              = 'BESS_SOC_LOW',
  BESS_SOC_HIGH             = 'BESS_SOC_HIGH',
  BESS_TEMP_HIGH            = 'BESS_TEMP_HIGH',
  BESS_FAULT                = 'BESS_FAULT',
  FVE_CURTAILED             = 'FVE_CURTAILED',
  FVE_INVERTER_FAULT        = 'FVE_INVERTER_FAULT',
  GRID_EXPORT_LIMIT_REACHED = 'GRID_EXPORT_LIMIT_REACHED',
  GRID_IMPORT_LIMIT_REACHED = 'GRID_IMPORT_LIMIT_REACHED',
  SETPOINT_DEVIATION        = 'SETPOINT_DEVIATION',
  SETPOINT_UNACHIEVABLE     = 'SETPOINT_UNACHIEVABLE',
  FALLBACK_ACTIVATED        = 'FALLBACK_ACTIVATED',
  OPERATING_MODE_NOT_HONORED = 'OPERATING_MODE_NOT_HONORED',
  SCHEDULE_SLOT_MISSED      = 'SCHEDULE_SLOT_MISSED',
  CONSTRAINT_VIOLATION      = 'CONSTRAINT_VIOLATION',
}

RejectionCode

enum RejectionCode {
  CONSTRAINT_VIOLATION       = 'CONSTRAINT_VIOLATION',
  INVALID_COMMAND            = 'INVALID_COMMAND',
  INVALID_PAYLOAD            = 'INVALID_PAYLOAD',
  DEVICE_OFFLINE             = 'DEVICE_OFFLINE',
  DEVICE_FAULT               = 'DEVICE_FAULT',
  UNSUPPORTED_FOR_TOPOLOGY   = 'UNSUPPORTED_FOR_TOPOLOGY',
}

ScheduleStatus

enum ScheduleStatus {
  PENDING             = 'PENDING',
  ACCEPTED            = 'ACCEPTED',
  ACTIVE              = 'ACTIVE',
  COMPLETED           = 'COMPLETED',
  CANCELLED           = 'CANCELLED',
  REJECTED            = 'REJECTED',
  PARTIALLY_EXECUTED  = 'PARTIALLY_EXECUTED',
  EXPIRED             = 'EXPIRED',
  FAILED              = 'FAILED',
}

Plant / site / device identifier conventions

IdentifierWhere usedMaps to
siteId (envelope)All messagesPlant's externalPlantId — the partner-facing ID the Voke operator assigns to the plant.
deviceId (payload)DeviceCommandPayload, SiteAssetDeclaration, SiteTopologyPayload, DeviceTelemetry, AlarmPayloadSub-device's externalId — the short operator-assigned identifier for a physical component (R1, M1, etc.).

Only DeviceCommandPayload and SiteAssetDeclaration payloads carry device-scoped identifiers. All other commands and events address the site as a whole through the envelope's siteId.


Versioning

The version field is a literal string, currently '1.1'. Partners should check this field on every incoming message and reject (nack with requeue = false) any envelope with an unrecognised version. Voke will increment the version string for any breaking change to the envelope or a payload schema.


Integrity and ordering guarantees live in VCP message integrity.

On this page