Skip to main content
When a device manufacturer rejects a request, the API returns a stable canonical code, a fixed HTTP status, and a customer-safe message. Every code on this page is API-owned: the manufacturer’s raw text is never surfaced, so the wording is the same whether the rejection comes from one manufacturer or another. Branch on error.code, not on message, and you will not see drift. The envelope is the standard one. The code rides at error.code, the status is the HTTP status, the message at error.message is the line below, and error.details carries any structured context the platform forwarded from the device’s response.
{
  "success": false,
  "error": {
    "code": "DEVICE_OFFLINE",
    "message": "The device is currently offline at the manufacturer.",
    "details": {}
  },
  "meta": {
    "requestId": "req_8a2Bf3kP",
    "timestamp": "2026-06-01T10:30:00.000Z",
    "path": "/battery/device_abc123",
    "latencyMs": 14
  }
}

Authentication and account

These codes appear when the manufacturer rejects the stored credentials for the linked account. Re-link the device through Link UI to capture a fresh credential.
CodeHTTPMessageWhat to do
INVALID_CREDENTIALS401The device manufacturer rejected the supplied credentials.Send the end user back through Link UI to re-authenticate.
INVALID_API_KEY401The device manufacturer rejected the supplied API key.The end user’s manufacturer key has been revoked or rotated. Capture a new one via Link UI.
INVALID_MFA_CODE403The multi-factor authentication code was not accepted.The code expired or was wrong. Restart the linking flow.
MFA_REQUIRED403The device manufacturer requires multi-factor authentication.Restart the linking flow so the end user can complete MFA.
ACCOUNT_LOCKED403The manufacturer account is locked.The end user must unlock the account directly with the manufacturer before re-linking.
UNSUPPORTED_CREDENTIAL_TYPE400The supplied credential type is not supported for this device.The credential captured during linking does not match what this manufacturer accepts. Re-run Link UI for the device.
UNSUPPORTED_AUTH_PATH400The requested authentication method is not supported.The auth method the link flow tried is not enabled for this manufacturer. Re-run Link UI.
CREDENTIAL_NOT_FOUND404No stored credential was found for this device.The link record is missing. Re-link the device through Link UI.

Device discovery

These codes name a problem locating the device on the manufacturer’s side.
CodeHTTPMessageWhat to do
DEVICE_NOT_FOUND404No matching device was found for the supplied details.The manufacturer no longer recognises this device, often because it was removed from the end user’s account. Re-link to refresh.
NO_DEVICES_FOUND404No devices were found on the manufacturer account.The linked account has no devices the manufacturer is willing to share. Check the end user’s manufacturer app, then re-link.
DEVICE_OFFLINE410The device is currently offline at the manufacturer.Transient. The device has lost its connection to the manufacturer cloud. Retry once the device is back online.
DEVICE_UNAUTHORIZED403The account is not authorised to control this device.The end user can read the device but cannot send commands. They must grant control in the manufacturer app, then re-link.

Command and parameter

These codes name a request the manufacturer would not run. The request body needs to change, not the timing.
CodeHTTPMessageWhat to do
COMMAND_FAILED400The device manufacturer could not complete the command.The manufacturer accepted the request and then could not carry it out. Inspect the action and retry once any condition the device flagged has cleared.
COMMAND_NOT_SUPPORTED400The device manufacturer does not support this command.This command is not available on the device today. Pick another command from the device’s commands map.
BIND_NOT_SUPPORTED400The device manufacturer does not support linking this device.The end user’s device cannot be linked to a third-party API. There is no workaround inside Amps.
UNKNOWN_SETTING400One or more settings are not recognised for this device.A setting key in the request body is not valid for this device. Check the device’s settings map and drop unknown keys.
INVALID_PARAMETERS400One or more request parameters were invalid.A request parameter failed the manufacturer’s own validation. Fix the value and resubmit.
INVALID_OEM_PARAMETERS400One or more request parameters were rejected by the device manufacturer.The manufacturer rejected a value that passed the API’s own validation. Check the device’s capability bounds and resubmit.
EXECUTION_NOT_SUPPORTED422The requested execution mode is not supported for this device.The request shape (immediate, scheduled, or windowed) is not in the command’s execution array. Pick a supported shape.
INVALID_TIME_WINDOW422The requested time window is invalid.The start/end pair violates the time-window contract. Inspect error.details.reason for the specific issue.

State conflict

These codes are 409s. The request is well-formed but the device is in a state that overrides or blocks it. Most are resolvable with onConflict: cancel_and_replace on the next push.
CodeHTTPMessageWhat to do
SCHEDULER_ACTIVE409A schedule is currently active on the device and must be cleared first.Resubmit with onConflict: cancel_and_replace to clear the conflicting schedule rows. error.details.recoveryStrategies lists the strategies the platform supports for this device. See conflict resolution.
SCHEDULER_FULL409The device schedule is full.The merged schedule would exceed the manufacturer’s slot limit. Cancel an existing schedule or ask the end user to clear rows from the manufacturer app.
MODE_OVERRIDDEN409The device is in a state that overrides the requested mode. Clear the controlling schedule or override first.Another active mode or schedule on the device is dominant. Clear it before the command will take effect.
VPP_LOCKED409The device is currently locked by another control programme and cannot accept this command.The device has been enlisted in a manufacturer-side virtual-power-plant or aggregator programme that takes precedence. The end user must opt out at the manufacturer before Amps can write.

Transport and capacity

These codes are transient. The device manufacturer is reachable but is briefly unable to accept the request. Retry with exponential backoff.
CodeHTTPMessageWhat to do
NETWORK_ERROR502The device manufacturer could not be reached.A transport-level failure between the platform and the manufacturer cloud. Retry.
RATE_LIMITED429The device manufacturer is rate limiting requests. Please try again shortly.The manufacturer’s per-account or per-device limit was hit. Back off and retry.
SERVICE_UNAVAILABLE503The device manufacturer service is temporarily unavailable.The manufacturer cloud is down or refusing requests. Retry with backoff.
TIMEOUT504The device manufacturer did not respond in time.The manufacturer accepted the request but did not respond before the API’s timeout. Retry.
SIMULATED_FAILURE503Simulated failure (sandbox).Reached only in sandbox via sandbox: { result: "failed", errorCode }. Used to exercise error-handling without touching a real device.
UNKNOWN_ERROR500The device manufacturer rejected the request.The manufacturer responded with a state the platform does not yet have a stable code for. meta.requestId ties the request to platform logs; surface it when escalating.

When to retry

ClassRetry?
Transport (NETWORK_ERROR, RATE_LIMITED, SERVICE_UNAVAILABLE, TIMEOUT)Yes, with exponential backoff.
Device offline (DEVICE_OFFLINE)Yes, after a delay. The device is briefly unreachable.
State conflict (SCHEDULER_ACTIVE, MODE_OVERRIDDEN, VPP_LOCKED)Resolve via onConflict or by clearing the controlling state, then resubmit.
Auth (INVALID_CREDENTIALS, MFA_REQUIRED, ACCOUNT_LOCKED, CREDENTIAL_NOT_FOUND)No. Re-link the device.
Command (COMMAND_NOT_SUPPORTED, INVALID_PARAMETERS, EXECUTION_NOT_SUPPORTED)No. Fix the request body.
UNKNOWN_ERROROnce, then escalate with the requestId.
The platform never retries push commands on the manufacturer’s behalf. A command that runs minutes later is usually worse than one that fails, so the caller decides whether to resubmit, with what, and when.

Frequently asked questions

Why is the error message the same regardless of which manufacturer rejected the request?

The error message is API-owned. The platform translates the manufacturer’s response into one of the canonical codes on this page, and the message is fixed per code. This keeps customer-facing prose stable and avoids leaking manufacturer-internal language (codes, hostnames, product names) into the response. The manufacturer’s original text is retained in platform logs for diagnostics and is tied to the response through meta.requestId.

How do I tell a transient failure apart from a fatal one?

By the code, not by the HTTP status alone. Transport codes (NETWORK_ERROR, RATE_LIMITED, SERVICE_UNAVAILABLE, TIMEOUT) and DEVICE_OFFLINE are retryable. Auth and command codes are not retryable as-is: the credential needs refreshing or the request needs changing. State-conflict codes are resolvable with onConflict or by clearing the controlling state on the device. Treat UNKNOWN_ERROR as an unclassified manufacturer response: retry once and escalate if it persists.

What lives in error.details?

Structured context the platform forwarded from the manufacturer’s response, plus any platform-side context relevant to the rejection. For SCHEDULER_ACTIVE on a windowed push, that includes reason, description, overlappingGroups, existingGroupCount, recoveryStrategies, and detectedAt. For RATE_LIMITED, it may include a hint at the manufacturer’s reported retry window. For command codes, it typically carries the offending parameter and the device’s supported subset. The details shape is stable per code, so an integration can branch on it.

What if I receive a code that is not on this page?

The platform routes new manufacturer codes through UNKNOWN_ERROR until they have a stable canonical mapping, so callers never see a raw manufacturer code. If you see UNKNOWN_ERROR against a request that should have succeeded, surface meta.requestId and the path and contact support; that pair lets the platform team trace the request and decide whether the response deserves its own code.

Error envelope

The full code taxonomy, including request-validation and platform codes.

Conflict resolution

SCHEDULER_ACTIVE and onConflict in depth.

Handle a 409 conflict

Worked recovery patterns in code.

Battery cheat sheet

The canonical battery vocabulary in one page.