Integrator checklist
A spreadsheet version of this checklist is available for tracking your test runs: integrator-checklist.xlsx. The columns mirror the modules and tests on this page, with empty Result and Notes columns for you to fill in.
About this checklist
This is a self-attested conformance checklist for EMS integrators connecting to the Tensor Cloud battery optimization API. It complements the integration guide and the AsyncAPI specification by providing concrete pass criteria for each part of the protocol that an integrator can verify against their own gateway before requesting a production handover.
The checklist is structured as independent modules. Read Selecting your modules first to identify which modules apply to your project type and service mix, then work through each applicable module in order. The Connection, Topic addressing, Message envelope, Battery telemetry, Energy reporting, Alerts, and Backfill modules are required for every integrator.
Selecting your modules
Pick your project type, then add modules for each service you offer.
| Project type | Connection, Topic, Envelope, Battery telemetry, Energy, Alerts, Backfill | Solar | Site load | Constraints module |
|---|---|---|---|---|
| Standalone battery | required | - | - | Standalone |
| AC-link battery + PV | required | required | conditional | AC-link |
| DC-link battery + PV | required | required | conditional | DC-link |
| Service you offer | Add this module |
|---|---|
| Energy arbitrage | Arbitrage |
| FCR / ancillary services | FCR |
| Site load reporting | Site load |
Module: Connection
Applies to: all integrators.
C-1 - TLS to broker endpoint
Open a TLS connection to the documented broker hostname on port 8883. Validate the server certificate against the Tensor Cloud CA chain.
Pass criteria: connection established, server certificate verified, plaintext on :1883 refused.
C-2 - Per-gateway certificate uniqueness
Confirm each gateway is provisioned with its own client certificate. Attempt a second concurrent connection using the same certificate from a different host - AWS IoT Core disconnects the older session.
Pass criteria: each gateway has a unique certificate; opening a second concurrent connection with the same certificate disconnects the first.
C-3 - CA chain validation
EMS validates the broker certificate against the documented CA chain. Reject connections when the chain is broken or expired.
Pass criteria: invalid or self-signed broker certificate is rejected.
C-7 - QoS policy per topic class
EMS uses the QoS values documented for command topics, telemetry topics, and acknowledgement topics. Application-level acknowledgement on ack-cmd/{siteId} is required regardless of MQTT QoS.
Pass criteria: publish QoS matches the documented policy per topic class; the EMS sends an application-level CommandResponse for every command even when broker QoS would suggest no application ack is required.
C-8 - Reconnect with backoff
After a broker disconnect, the EMS reconnects with an exponential or otherwise capped backoff policy.
Pass criteria: reconnect storms are avoided; subscriptions are re-established before resuming publishes.
C-9 - Behavior when telemetry cannot be published
When the EMS cannot publish (broker unreachable, TLS error), it buffers telemetry locally rather than dropping it silently.
Pass criteria: buffered telemetry replays per the Backfill module. Cross-references BF-1.
Module: Topic addressing and identity
Applies to: all integrators.
T-1 - siteId format
Site identifiers are prefixed si_ followed by six lowercase alphanumeric characters (e.g. si_qcf9gn).
Pass criteria: every siteId used in publish or subscribe topics matches ^si_[a-z0-9]{6}$.
T-2 - gatewayId format
Gateway identifiers are prefixed gw_ followed by six lowercase alphanumeric characters.
Pass criteria: every gatewayId matches ^gw_[a-z0-9]{6}$.
T-3 - Publish topic patterns per channel
The EMS publishes only on the topics defined by the schema:
dt/{siteId}/{gatewayId}/{metric}/lifetimedt/{siteId}/{gatewayId}/{metric}/energy/{window}dt/{siteId}/{gatewayId}/{metric}/powerdt/{siteId}/{gatewayId}/{metric}/statedt/{siteId}/{gatewayId}/curtailmentdt/{siteId}/{gatewayId}/irradiationdt/{siteId}/{gatewayId}/alertdt/{siteId}/{gatewayId}/alert/activeack-cmd/{siteId}
Pass criteria: no publishes to topics outside this list.
T-4 - Subscribe topic patterns per channel
The EMS subscribes only to:
cmd/{siteId}/battery/powercmd/{siteId}/battery/fcr(FCR integrators only)ack-dt/{siteId}/{gatewayId}
Pass criteria: documented subscriptions match the list above. Wildcards (e.g. cmd/{siteId}/battery/+) are permitted as an alternative to exact topics.
T-5 - res_topic namespace
The res_topic field on every received command points back to the same site as the command's topic. AWS IoT Core enforces this at the broker layer: the gateway's certificate only authorizes publish into its own site's ack-cmd/{siteId} namespace, so any attempt by the EMS to publish a response to a mismatched res_topic is rejected at the broker.
Pass criteria: publishing a CommandResponse to the res_topic value returned in the command succeeds for legitimate commands; attempts to publish into a foreign site's namespace are rejected by the broker.
T-6 - ack-cmd and ack-dt namespaces
Command responses are published on ack-cmd/{siteId}. Telemetry feedback is received on ack-dt/{siteId}/{gatewayId}.
Pass criteria: EMS uses the correct namespace for each direction.
T-7 - ack-dt topic shape
Telemetry feedback arrives on ack-dt/{siteId}/{gatewayId} exactly, with no /telemetry suffix.
Pass criteria: EMS subscribes to ack-dt/{siteId}/{gatewayId}.
T-8 - Hot-standby acknowledgement
In hot-standby, only the active gateway acks commands and executes schedules. The standby gateway is subscribed to the command topic but stays silent until promoted. Primary/standby coordination is the EMS partner's responsibility (per the integration guide).
Pass criteria: under hot-standby, exactly one CommandResponse is sent per command, and exactly one gateway executes the schedule.
Module: Message envelope
Applies to: all integrators.
E-1 - schema_version present on every message
Every published payload includes schema_version, including AlertEvent and AlertState.
Pass criteria: schema_version is present on every message the EMS emits.
E-2 - schema_version value
schema_version is "2.2.0" for every message emitted under this revision of the spec. Tensor Cloud remains backwards-compatible with messages emitted under older spec revisions.
Pass criteria: every emitted schema_version is "2.2.0".
E-3 - message_id format
Every message_id matches ^msg_[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$.
Pass criteria: all generated message_id values pass the regex.
E-4 - command_id correlation
Every CommandResponse carries command_id equal to the originating command's message_id.
Pass criteria: for a representative sample of commands sent during testing, every response's command_id matches a command's message_id.
E-5 - Timestamps
All *_ts fields are ISO-8601 timestamps with a timezone offset (e.g. 2024-01-05T00:00:00.000+09:00).
Pass criteria: every emitted timestamp parses as ISO-8601 with offset and does not use bare Z if the documented convention is +09:00.
E-6 - additionalProperties policy
The schema allows unknown fields on every message family (additionalProperties: true) to preserve forward-compatibility. The EMS should still emit only fields defined in the schema; receivers on both sides should tolerate additions made by future schema revisions.
Pass criteria: every emitted payload contains only fields defined in the schema. The EMS parser does not crash or silently drop messages that include unknown fields.
Module: Battery telemetry
Applies to: all integrators.
BT-1 - Power telemetry shape
Battery power telemetry publishes on dt/{siteId}/{gatewayId}/{metric}/power with a PowerInstant payload.
Pass criteria: publishes validate against the schema; metric parameter is from the battery subset of the metric enum.
BT-2 - State telemetry shape
Battery state telemetry (SoC, etc.) publishes on dt/{siteId}/{gatewayId}/{metric}/state with a BatteryStateValue payload.
Pass criteria: publishes validate against the schema.
BT-3 - Telemetry cadence
Telemetry is published at the cadence documented by Tensor (typically: continuous, sub-minute for power and state).
Pass criteria: observed cadence matches documented expectation across a representative window.
BT-4 - Telemetry freshness on reconnect
After a disconnect, the EMS prioritizes the latest sample over backfilled history when publishing resumes.
Pass criteria: the first message published after reconnect carries the freshest sample, not the oldest buffered one. Cross-references Backfill module.
BT-5 - Telemetry feedback consumption
The EMS subscribes to ack-dt/{siteId}/{gatewayId} and reacts to TelemetryFeedback messages.
Pass criteria: EMS demonstrates handling of status: "ok" and status: "error" feedback.
BT-6 - Telemetry feedback retry behavior
The EMS has a documented retry / drop policy in response to telemetry feedback errors.
Pass criteria: EMS behavior matches the documented policy.
BT-7 - TelemetryFeedback conditional fields
TelemetryFeedback carries code and detail when status is error, and omits both when status is ok. The schema enforces this via if/then/else.
Pass criteria: EMS-side parser handles both shapes (no crash, no silent drop) and treats receipt of code/detail on ok as a protocol violation.
Module: Energy reporting
Applies to: all integrators.
EN-1 - At least one energy reading stream per required metric
For each energy-flow metric required by the integrator's project type (see the project-type constraints module), the EMS emits either a windowed energy stream, a lifetime counter, or both.
Pass criteria: every required energy-flow metric has at least one active stream.
EN-2 - Lifetime counter monotonicity
Values on dt/{siteId}/{gatewayId}/{metric}/lifetime never decrease across messages (except for documented counter-reset events).
Pass criteria: monotonic non-decreasing across a representative observation window.
EN-3 - Lifetime counter units
Lifetime payloads use kWh per the EnergyLifetime schema.
Pass criteria: all lifetime values are kWh.
EN-4 - Windowed bucket boundaries
For dt/{siteId}/{gatewayId}/{metric}/energy/{window}, the start_ts and end_ts boundaries in the payload match the {window} parameter (5min, 30min, 60min, etc.).
Pass criteria: every windowed payload's start_ts/end_ts matches the window length implied by the topic.
EN-5 - Late-arrival policy
The EMS publishes windowed energy values only after the window has closed. Tensor Cloud has no late-arrival cutoff - samples are accepted regardless of how long after the window closed they arrive.
Pass criteria: EMS publishes windowed values strictly after window close. Late-arriving samples (from backfill or recovery) are still published rather than dropped.
Module: Alerts
Applies to: all integrators.
A-1 - alertEvent emission
Transient alert events publish on dt/{siteId}/{gatewayId}/alert conforming to AlertEvent.
Pass criteria: every emitted code is a member of AlertCode.
A-2 - alertState clearing semantics
When an alert resolves, the next alertState snapshot omits it. alertState is a periodic full snapshot (at least every 10 minutes, and at startup), so re-subscribing consumers and recovery from missed events both rely on the next snapshot rather than any retained message.
Pass criteria: after a resolved alert, the next alertState does not include the resolved alert.
A-4 - AlertCode enumeration coverage
All codes emitted by the EMS appear in the schema's AlertCode enum.
Pass criteria: no proprietary alert codes are emitted on these topics.
Module: Backfill and offline recovery
Applies to: all integrators.
BF-1 - Reconnect replay ordering
After a network outage, the EMS resumes publishing live samples first. Only when no live sample is waiting to be published does it start replaying buffered history.
Pass criteria: observed traffic after reconnect shows live samples ahead of backfill; backfill yields to fresh live samples whenever they arrive.
BF-2 - Maximum backlog age
The EMS retains telemetry locally for up to 7 days. Buffered samples older than 7 days are dropped or summarized.
Pass criteria: EMS drops or summarizes buffered samples older than 7 days; samples younger than 7 days are replayed.
BF-3 - Duplicate handling on reconnect
When the EMS resends a sample that may have already been delivered, it reuses the same message_id as the original publish. Tensor Cloud deduplicates on message_id.
Pass criteria: every resend of a given sample carries the same message_id as its first publish attempt. The EMS does not mint a new message_id for a retry.
Module: Arbitrage (BatteryPowerCommand)
Applies to: integrators offering the energy arbitrage service.
AR-1 - Receive power command
EMS receives a representative BatteryPowerCommand on cmd/{siteId}/battery/power.
Pass criteria: message delivered, parsed without error.
AR-2 - Command shape per schema
The payload contains schema_version, message_id, issue_ts, res_topic, and a control object with priority (optional) and schedule (required).
Pass criteria: payload validates against BatteryPowerCommand.
AR-3 - ack-cmd success response
On accepting a command, the EMS publishes on ack-cmd/{siteId} with status: "ok", command_id equal to the command's message_id, and schema_version: "2.2.0".
Pass criteria: response validates against CommandResponse; errors is absent.
AR-4 - ack-cmd error response shape
On rejecting a command, the EMS publishes status: "error" with an errors array. Each error has code (from ErrorCode) and detail. Use field_path where applicable.
Pass criteria: response uses detail for each error; every code is from ErrorCode.
AR-5 - errors conditional on status
When status is error, the errors array is present and contains at least one item. When status is ok, the errors field is absent. The schema enforces both via an if/then/else constraint, so this is also a schema-validation check.
Pass criteria: errors present (with ≥ 1 item) on status: "error"; errors absent on status: "ok".
AR-6 - ErrorCode mapping
The EMS uses the ErrorCode value documented in the integration guide for each class of validation failure: MESSAGE_MALFORMED for unparseable JSON, MISSING_FIELD for absent required fields, TYPE_MISMATCH for wrong types, FIELD_OUT_OF_RANGE for out-of-range numeric values, TIME_WINDOW_INVALID for empty schedules / overlapping intervals / inverted start_ts/end_ts, EXPIRED for past-end schedules, DUPLICATE on message_id collision, RESOURCE_UNAVAILABLE for equipment unavailability, INTERNAL_ERROR for anything else.
Pass criteria: for each failure class above, the EMS emits the documented ErrorCode. The same failure class always produces the same code across runs.
AR-7 - Schedule gaps
Send a schedule with a time gap between intervals.
Pass criteria: during the gap, the EMS falls back to any previously valid command covering that time slot. If none, the EMS uses power_kw = 0 (standby).
AR-8 - Overlaps within a command
Send a schedule whose intervals overlap each other.
Pass criteria: EMS rejects with a documented ErrorCode and field_path: "control.schedule".
AR-9 - Empty schedule
Send a command with an empty schedule array.
Pass criteria: EMS rejects with a documented ErrorCode and field_path: "control.schedule". The schema also enforces minItems: 1 on schedule, so this is a schema-validation check.
AR-10 - Priority and issue_ts tie-breaking
Send two overlapping commands. With different priority, the higher priority wins. With identical priority, the later issue_ts wins. With identical priority and issue_ts, the last-received command (last over the wire) wins.
Pass criteria: EMS executes the winning command for each tie-break scenario and acknowledges all commands.
AR-11 - Stale-schedule rejection
Send a command whose schedule's last interval end_ts is in the past.
Pass criteria: EMS rejects with a documented ErrorCode. (Old issue_ts alone is not a rejection reason - issue_ts is for tie-breaking only.)
AR-12 - Per-slot supersession
Send a command that overlaps only part of an in-progress schedule. The new command should replace the existing schedule only for the overlapping time slots; non-overlapping intervals of the older command remain in effect.
Pass criteria: at execution time, the EMS uses the new command's setpoints for the overlapping slots and continues with the older command's setpoints for non-overlapping slots. There is no explicit power-command cancellation - to override a power schedule, Tensor Cloud sends a new command (with power_kw = 0 or other values) at appropriate priority.
Module: FCR (BatteryFcrCommand)
Applies to: integrators offering FCR or ancillary services.
FC-1 - Receive FCR command
EMS receives a representative BatteryFcrCommand on cmd/{siteId}/battery/fcr.
Pass criteria: message delivered, parsed without error.
FC-2 - action defaults to execute
When the action field is absent on a schedule item, the EMS treats it as execute.
Pass criteria: matches the schema's default.
FC-3 - capacity_kw required for execute
For schedule items with action: "execute" (the default), the EMS requires capacity_kw and rejects items missing it. The schema enforces this via if/then/else on control.action.
Pass criteria: EMS rejects with a documented ErrorCode and field_path pointing at the offending item.
FC-4 - capacity_kw forbidden on cancel
For action: "cancel", capacity_kw is forbidden on schedule items. The schema enforces this via the same if/then/else on control.action.
Pass criteria: EMS rejects a cancel command carrying capacity_kw with a documented ErrorCode.
FC-7 - FCR baseline composition with arbitrage
Arbitrage and FCR commands compose rather than supersede: an FCR command sets the capacity offered for frequency response, while a concurrent BatteryPowerCommand shifts the baseline. If the EMS holds an FCR schedule for 1500 kW and receives a power command of −100 kW for the same time slots, the baseline shifts to −100 kW (per integration guide).
Pass criteria: when both an FCR schedule and an arbitrage schedule cover the same time slot, the EMS applies the arbitrage value as the baseline and uses the FCR capacity for frequency response on top of it.
Module: Solar
Applies to: AC-link and DC-link integrators with on-site PV.
SO-1 - Irradiation telemetry
EMS publishes on dt/{siteId}/{gatewayId}/irradiation conforming to Irradiation.
Pass criteria: publishes validate against the schema.
SO-2 - Irradiation cadence
EMS publishes irradiation at the documented cadence (typically continuous during daylight, paused or zero at night).
Pass criteria: observed cadence matches documented expectation.
SO-3 - Curtailment reporting
EMS publishes curtailment on dt/{siteId}/{gatewayId}/curtailment conforming to Curtailment.
Pass criteria: publishes validate against the schema; curtailment is reported whenever the inverter is operating below available irradiation.
SO-4 - Solar power telemetry metrics
PV-related metrics from the metric enum (pv_to_inverter_ac, etc.) publish on the power topic.
Pass criteria: every PV metric the EMS supports is emitted on dt/{siteId}/{gatewayId}/{metric}/power.
SO-5 - inverter_net_output_ac definition
EMS emits inverter_net_output_ac as the total AC-side output of the inverter - the sum of inverter_to_load_ac and inverter_to_grid_ac. Values are non-negative.
Pass criteria: emitted values are ≥ 0 and equal inverter_to_load_ac + inverter_to_grid_ac.
Module: Site load
Applies to: integrators reporting load telemetry (typically AC-link with on-site load).
SL-1 - Load power telemetry
EMS publishes load-side power metrics on dt/{siteId}/{gatewayId}/{metric}/power.
Pass criteria: publishes validate against PowerInstant.
SL-2 - Load metric naming
EMS uses inverter_to_load_ac, not inverter_to_load_kwh.
Pass criteria: every load-related metric is the canonical name from the metric enum.
SL-3 - Grid-charging metric (DC-link)
For DC-link sites where grid charging is possible, the EMS emits grid_to_inverter_ac.
Pass criteria: the metric is emitted whenever the site can be charged from grid.
SL-4 - Grid-charging metric (AC-link)
For AC-link sites where grid charging is possible, the EMS emits grid_to_battery_ac.
Pass criteria: grid_to_battery_ac is emitted whenever the site can be charged from grid.
Module: Project-type constraints
Applies to: all integrators (run the sub-module that matches your project type).
Standalone
- Required metrics: battery-only metrics from the metric enum.
- Forbidden metrics: solar metrics, load metrics, DC-link-only metrics.
Test PT-S - Standalone metric set. The EMS publishes only metrics from the required + permitted set. AWS IoT Core enforces topic scoping at the broker layer based on the gateway's certificate, so attempts to publish unauthorized metric topics are rejected at connection time and never reach Tensor Cloud.
AC-link
- Required metrics: AC-link battery and solar metrics per the integration guide.
- Conditional metrics: load and grid-charging metrics where applicable.
- Forbidden metrics: DC-link-only metrics.
Test PT-AC - AC-link metric set. The EMS publishes only AC-link required + permitted metrics. No DC-link-only metrics appear on any telemetry topic.
DC-link
- Required metrics: DC-link battery and solar metrics per the integration guide, including
grid_to_inverter_acwhen grid charging is possible. - Conditional metrics: load metrics where applicable.
- Forbidden metrics: AC-link-only metrics.
Test PT-DC - DC-link metric set. The EMS publishes only DC-link required + permitted metrics. No AC-link-only metrics appear on any telemetry topic.