You have two ways to cancel. Use the cancel endpoint to drop a scheduled action by ID; it only works while the action is in scheduled state. Or use onConflict: "cancel_and_replace" on a fresh push to cancel the colliding action and apply the new one in a single round-trip.
When the new intent is also a push, fold the cancellation into the new request. This avoids a race where the original fires between cancel and re-push.
If the colliding action is already acknowledged, the cancel half of cancel_and_replace fails. You get 409 CONFLICT with the in-flight action ID.
{ "success": false, "error": { "code": "CONFLICT", "message": "The conflicting action is already in progress and cannot be cancelled. Wait for it to complete or fail.", "details": { "conflictingActionIds": ["act_inflight_002"] } }, "meta": { "requestId": "req_3dC6hOsT", "timestamp": "2026-05-08T11:32:00.000Z", "path": "/battery/dev_abc123", "latencyMs": 18 }}
Poll the in-flight action. Once it reaches completed or failed, retry the push.