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.
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.| Code | HTTP | Message | What to do |
|---|---|---|---|
INVALID_CREDENTIALS | 401 | The device manufacturer rejected the supplied credentials. | Send the end user back through Link UI to re-authenticate. |
INVALID_API_KEY | 401 | The 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_CODE | 403 | The multi-factor authentication code was not accepted. | The code expired or was wrong. Restart the linking flow. |
MFA_REQUIRED | 403 | The device manufacturer requires multi-factor authentication. | Restart the linking flow so the end user can complete MFA. |
ACCOUNT_LOCKED | 403 | The manufacturer account is locked. | The end user must unlock the account directly with the manufacturer before re-linking. |
UNSUPPORTED_CREDENTIAL_TYPE | 400 | The 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_PATH | 400 | The 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_FOUND | 404 | No 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.| Code | HTTP | Message | What to do |
|---|---|---|---|
DEVICE_NOT_FOUND | 404 | No 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_FOUND | 404 | No 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_OFFLINE | 410 | The 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_UNAUTHORIZED | 403 | The 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.| Code | HTTP | Message | What to do |
|---|---|---|---|
COMMAND_FAILED | 400 | The 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_SUPPORTED | 400 | The 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_SUPPORTED | 400 | The 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_SETTING | 400 | One 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_PARAMETERS | 400 | One or more request parameters were invalid. | A request parameter failed the manufacturer’s own validation. Fix the value and resubmit. |
INVALID_OEM_PARAMETERS | 400 | One 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_SUPPORTED | 422 | The 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_WINDOW | 422 | The 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 withonConflict: cancel_and_replace on the next push.
| Code | HTTP | Message | What to do |
|---|---|---|---|
SCHEDULER_ACTIVE | 409 | A 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_FULL | 409 | The 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_OVERRIDDEN | 409 | The 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_LOCKED | 409 | The 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.| Code | HTTP | Message | What to do |
|---|---|---|---|
NETWORK_ERROR | 502 | The device manufacturer could not be reached. | A transport-level failure between the platform and the manufacturer cloud. Retry. |
RATE_LIMITED | 429 | The 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_UNAVAILABLE | 503 | The device manufacturer service is temporarily unavailable. | The manufacturer cloud is down or refusing requests. Retry with backoff. |
TIMEOUT | 504 | The device manufacturer did not respond in time. | The manufacturer accepted the request but did not respond before the API’s timeout. Retry. |
SIMULATED_FAILURE | 503 | Simulated failure (sandbox). | Reached only in sandbox via sandbox: { result: "failed", errorCode }. Used to exercise error-handling without touching a real device. |
UNKNOWN_ERROR | 500 | The 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
| Class | Retry? |
|---|---|
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_ERROR | Once, then escalate with the requestId. |
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 throughmeta.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 throughUNKNOWN_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.
Related
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.