Orkes logo image
Product
Platform
Orkes Platform thumbnail
Orkes Platform
Orkes Agentic Workflows
Orkes Conductor Vs Conductor OSS thumbnail
Orkes vs. Conductor OSS
Orkes Cloud
How Orkes Powers Boat Thumbnail
How Orkes Powers BOAT
Try enterprise Orkes Cloud for free
Enjoy a free 14-day trial with all enterprise features
Start for free
Capabilities
Microservices Workflow Orchestration icon
Microservices Workflow Orchestration
Enable faster development cycles, easier maintenance, and improved user experiences.
Realtime API Orchestration icon
Realtime API Orchestration
Enable faster development cycles, easier maintenance, and improved user experiences.
Event Driven Architecture icon
Event Driven Architecture
Create durable workflows that promote modularity, flexibility, and responsiveness.
Human Workflow Orchestration icon
Human Workflow Orchestration
Seamlessly insert humans in the loop of complex workflows.
Process orchestration icon
Process Orchestration
Visualize end-to-end business processes, connect people, processes and systems, and monitor performance to resolve issues in real-time
Use Cases
By Industry
Financial Services icon
Financial Services
Secure and comprehensive workflow orchestration for financial services
Media and Entertainment icon
Media and Entertainment
Enterprise grade workflow orchestration for your media pipelines
Telecommunications icon
Telecommunications
Future proof your workflow management with workflow orchestration
Healthcare icon
Healthcare
Revolutionize and expedite patient care with workflow orchestration for healthcare
Shipping and logistics icon
Shipping and Logistics
Reinforce your inventory management with durable execution and long running workflows
Software icon
Software
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Aenean leo mauris, laoreet interdum sodales a, mollis nec enim.
Docs
Developers
Learn
Blog
Explore our blog for insights into the latest trends in workflow orchestration, real-world use cases, and updates on how our solutions are transforming industries.
Read blogs
Check out our latest blog:
Conductor CLI Guide: Register, Run, Retry, and Recover Durable Workflows Without Leaving Your Terminal 💻
Customers
Discover how leading companies are using Orkes to accelerate development, streamline operations, and achieve remarkable results.
Read case studies
Our latest case study:
Twilio Case Study Thumbnail
Orkes Academy New!
Master workflow orchestration with hands-on labs, structured learning paths, and certification. Build production-ready workflows from fundamentals to Agentic AI.
Explore courses
Featured course:
Orkes Academy Thumbnail
Events icon
Events
Videos icons
Videos
In the news icon
In the News
Whitepapers icon
Whitepapers
About us icon
About Us
Pricing
Get a demo
Signup
Slack FaviconDiscourse Logo icon
Get a demo
Signup
Slack FaviconDiscourse Logo icon
Orkes logo image

Company

Platform
Careers
HIRING!
Partners
About Us
Legal Hub
Security

Product

Cloud
Platform
Support

Community

Docs
Blogs
Events

Use Cases

Microservices Workflow Orchestration
Realtime API Orchestration
Event Driven Architecture
Agentic Workflows
Human Workflow Orchestration
Process Orchestration

Compare

Orkes vs Camunda
Orkes vs BPMN
Orkes vs LangChain
Orkes vs Temporal
Twitter or X Socials linkLinkedIn Socials linkYouTube Socials linkSlack Socials linkGithub Socials linkFacebook iconInstagram iconTik Tok icon
© 2026 Orkes. All Rights Reserved.
Back to Blogs

Table of Contents

Share on:Share on LinkedInShare on FacebookShare on Twitter
Worker Code Illustration

Get Started for Free with Dev Edition

Signup
Back to Blogs

Expedite Tesla Powerwall Payback Period with Automated Solar Production Curtailment During Negative Feed-In Pricing with Australia’s Amber Electric and Orkes

JC
Jonathan Cochran
Principal Solutions Engineer
Last updated: February 18, 2026
February 18, 2026
5 min read

Related Blogs

Conductor CLI Guide: Register, Run, Retry, and Recover Durable Workflows Without Leaving Your Terminal 💻

Mar 24, 2026

Conductor CLI Guide: Register, Run, Retry, and Recover Durable Workflows Without Leaving Your Terminal 💻

Workflow Versioning and Backward Compatibility: Stop Breaking Long-Running Executions

Mar 17, 2026

Workflow Versioning and Backward Compatibility: Stop Breaking Long-Running Executions

How to Build an AI-Powered Support Ticket Triage Workflow with Orkes Conductor

Mar 17, 2026

How to Build an AI-Powered Support Ticket Triage Workflow with Orkes Conductor

Ready to Build Something Amazing?

Join thousands of developers building the future with Orkes.

Start for free

Cover illustration for the article Expedite Tesla Powerwall Payback Period with Automated Solar Production Curtailment During Negative Feed-In Pricing with Australia’s Amber Electric and Orkes

Section 1: Use Case

This Orkes workflow optimises Tesla Powerwall energy export behavior when connected to Australia’s grid with Amber Electric’s wholesale pricing energy plan.

When feed-in rates drop below 0 cents per kWh, you’re actually paying to export excess solar energy to the grid; reducing overall savings and ultimately delaying your system’s payback period. Monitoring price changes and manually updating battery export settings is not practical, given frequent fluctuations.

Orkes Conductor solves this by orchestrating API calls, evaluating real-time conditions, and applying configuration changes to your system - only when needed. The workflow continuously aligns battery export behavior with current pricing, without manual intervention or unnecessary updates.

In this tutorial, you will build a workflow that:

  • Retrieves the current Tesla Powerwall battery export setting (via NetZero’s API proxy)
  • Retrieves the current feed-in price from Amber Electric
  • Evaluates whether exporting energy should be enabled or disabled based on pricing
  • Updates your Powerwall’s export configuration only when a change is required

A screenshot of negative feed-in price

Section 2: Deploy the Tesla Powerwall Curtailment Workflow

Here’s the workflow you’ll build:

A screenshot of the tesla powerwall curtailment workflow.

Workflow Overview

  • The workflow begins with a Fork/Join which executes two HTTP tasks in parallel:
    • One retrieves the Powerwall’s current export configuration via NetZero API gateway
    • The other retrieves the current feed-in price per kWh from Amber’s pricing API
  • Each branch uses an Inline task to validate and extract the required values from the HTTP responses
  • A Join task waits for both forks and combines their outputs
  • A Switch task uses a simple JavaScript (GraalJS) expression to decide whether a battery export setting update is required
  • When an update is required, the workflow sends a POST request to the NetZero API using an HTTP task

Prerequisites

Before you begin, ensure you have access to the following:

  • A free Orkes Developer Edition account
  • A free account on NetZero (must provide existing Tesla credentials to sign in)
  • An existing Amber Electric energy account

Step 1: Get Amber Credentials and Store as Secrets in Orkes

Amber provides dynamic power prices that fluctuate based on the real-time wholesale energy market. In this workflow, we use the Amber energy pricing API to get the current feed-in price.

To authenticate API requests, you need an Amber API token and your associated site ID. These values are stored as secrets in Orkes Conductor to prevent sensitive data from being exposed in the workflow definition.

Get the Amber API Token

  1. Open a browser window, navigate to app.amber.com.au and sign in to your Amber account.
  2. On the left hand menu, select For Developers
  3. Toggle the DEVELOPER MODE switch to the on position
  4. Select Generate a new Token, provide an application name such as Orkes, then hit Generate
  5. Copy the resulting token (including the psk_) noting that the token will only be shown this one time before it is permanently hidden
  6. Navigate back to your Orkes Developer Edition account and select Definitions > Secrets on the left hand menu
  7. Select Add secret, enter the secret name precisely as amber_api_token then paste the API token as the Secret value. Hit Add

A screenshot of Conductor's secrets UI/adding a secret in Orkes Conductor

Get the Amber site ID

The site ID identifies the location for which pricing data is retrieved. It can be retrieved by making an API call using tools like Postman or cURL.

To get the Site ID:

  1. Open an API client such as Postman or cURL
  2. Send a GET request to https://api.amber.com.au/v1/sites
  3. Add the Authorization header and set the value to: Bearer <YOUR-AMBER-API-TOKEN> and send the request
  4. Copy the SiteID from the API response
  5. Now repeat the steps detailed above and create another secret with
    • Secret name exactly as amber_site_id
    • Secret value as the Amber site ID

The workflow definition created later in this tutorial will reference these secrets.

Step 2: Get API token and Energy System ID from NetZero

NetZero is an intelligent home energy management system. It enables programmatic control of your Tesla Powerwall with simplified API calls. In the following steps, you will fetch a NetZero API token and Energy System ID and create corresponding secrets in Orkes.

Get the NetZero API Token and Energy system ID (Site ID)

To get the API token and Energy system ID:

  1. Open a browser and navigate to app.netzero.energy
  2. Hit Sign in with Tesla, then sign in with your Tesla account credentials on the resulting sign in page
  3. Once logged into the NetZero interface, navigate to Settings on the bottom menu and then Developer API
  4. Copy the API token and create a corresponding Secret in Orkes:
    • Secret name exactly as netzero_api_token
    • Secret value your NetZero API token
  5. Copy the Energy system ID and create a corresponding Secret in Orkes:
    • Secret name exactly as netzero_site_id
    • Secret value your NetZero Energy system ID

A screenshot of the NetZero Developer screen

Step 3: Create Workflow in Orkes Conductor

Conductor workflows can be created in different ways such as writing workflows as code, APIs, importing from BPMNs, and using Conductor UI. In this tutorial, we will use the Orkes Conductor UI to build the workflow.

To create a workflow:

  1. Go to Definitions > Workflow from the left navigation menu on your Conductor cluster.
  2. Select + Define workflow
  3. In the Code tab, paste the following code;
json
{
  "name": "TeslaPowerwallSolarCurtailment",
  "description": "Prevents export to grid during times of negative feed-in rates when paired with Amber Electric's wholesale energy plan",
  "version": 1,
  "tasks": [
    {
      "name": "fork",
      "taskReferenceName": "fork_doGets",
      "inputParameters": {},
      "type": "FORK_JOIN",
      "decisionCases": {},
      "defaultCase": [],
      "forkTasks": [
        [
          {
            "name": "http",
            "taskReferenceName": "http_getCurrentBatteryExportSetting",
            "inputParameters": {
              "uri": "https://api.netzero.energy/api/v1/${workflow.secrets.netzero_site_id}/config",
              "method": "GET",
              "accept": "application/json",
              "contentType": "application/json",
              "encode": true,
              "hedgingConfig": {
                "maxAttempts": 3
              },
              "headers": {
                "Authorization": "Bearer ${workflow.secrets.netzero_api_token}"
              }
            },
            "type": "HTTP",
            "decisionCases": {},
            "defaultCase": [],
            "forkTasks": [],
            "startDelay": 0,
            "joinOn": [],
            "optional": false,
            "taskDefinition": {
              "createTime": 0,
              "updateTime": 0,
              "retryCount": 3,
              "timeoutSeconds": 0,
              "inputKeys": [],
              "outputKeys": [],
              "timeoutPolicy": "TIME_OUT_WF",
              "retryLogic": "FIXED",
              "retryDelaySeconds": 60,
              "responseTimeoutSeconds": 3600,
              "inputTemplate": {},
              "rateLimitPerFrequency": 0,
              "rateLimitFrequencyInSeconds": 1,
              "backoffScaleFactor": 1,
              "totalTimeoutSeconds": 0,
              "outputSchema": {
                "createTime": 0,
                "updateTime": 0,
                "name": "",
                "version": 1,
                "type": "JSON"
              },
              "enforceSchema": false
            },
            "defaultExclusiveJoinTask": [],
            "asyncComplete": false,
            "loopOver": [],
            "onStateChange": {},
            "permissive": false
          },
          {
            "name": "inline",
            "taskReferenceName": "inline_retrieveCurrentBatteryExportSetting",
            "inputParameters": {
              "expression": "(function () {\n  const response = $.getCurrentBatteryExportSettingOutput.response || {};\n  const body = response.body || {};\n\n  if (body.energy_exports || body.energy_exports === null)\n    return body.energy_exports;\n\n  throw \"unable to retrieve current battery export setting\";\n})();",
              "evaluatorType": "graaljs",
              "getCurrentBatteryExportSettingOutput": "${http_getCurrentBatteryExportSetting.output}"
            },
            "type": "INLINE",
            "decisionCases": {},
            "defaultCase": [],
            "forkTasks": [],
            "startDelay": 0,
            "joinOn": [],
            "optional": false,
            "defaultExclusiveJoinTask": [],
            "asyncComplete": false,
            "loopOver": [],
            "onStateChange": {},
            "permissive": false
          }
        ],
        [
          {
            "name": "http",
            "taskReferenceName": "http_getCurrentFeedInPrice",
            "inputParameters": {
              "uri": "https://api.amber.com.au/v1/sites/${workflow.secrets.amber_site_id}/prices/current",
              "method": "GET",
              "accept": "application/json",
              "contentType": "application/json",
              "encode": true,
              "hedgingConfig": {
                "maxAttempts": 3
              },
              "headers": {
                "Authorization": "Bearer ${workflow.secrets.amber_api_token}"
              }
            },
            "type": "HTTP",
            "decisionCases": {},
            "defaultCase": [],
            "forkTasks": [],
            "startDelay": 0,
            "joinOn": [],
            "optional": false,
            "taskDefinition": {
              "createTime": 0,
              "updateTime": 0,
              "retryCount": 3,
              "timeoutSeconds": 0,
              "inputKeys": [],
              "outputKeys": [],
              "timeoutPolicy": "TIME_OUT_WF",
              "retryLogic": "FIXED",
              "retryDelaySeconds": 60,
              "responseTimeoutSeconds": 3600,
              "inputTemplate": {},
              "rateLimitPerFrequency": 0,
              "rateLimitFrequencyInSeconds": 1,
              "backoffScaleFactor": 1,
              "totalTimeoutSeconds": 0,
              "outputSchema": {
                "createTime": 0,
                "updateTime": 0,
                "name": "",
                "version": 1,
                "type": "JSON"
              },
              "enforceSchema": false
            },
            "defaultExclusiveJoinTask": [],
            "asyncComplete": false,
            "loopOver": [],
            "onStateChange": {},
            "permissive": false
          },
          {
            "name": "inline",
            "taskReferenceName": "inline_retrieveCurrentFeedInPrice",
            "inputParameters": {
              "expression": "(function() {\n  const intervals = $.getCurrentFeedInPriceOutput.response.body;\n\n  for (let i = 0; i < intervals.length; i++) {\n    let interval = intervals[i];\n\n    if (interval.channelType === \"feedIn\")\n      return interval.perKwh;\n  }\n\n  throw \"unable to retrieve feed-in price per Kwh\";\n})();",
              "evaluatorType": "graaljs",
              "getCurrentFeedInPriceOutput": "${http_getCurrentFeedInPrice.output}"
            },
            "type": "INLINE",
            "decisionCases": {},
            "defaultCase": [],
            "forkTasks": [],
            "startDelay": 0,
            "joinOn": [],
            "optional": false,
            "defaultExclusiveJoinTask": [],
            "asyncComplete": false,
            "loopOver": [],
            "onStateChange": {},
            "permissive": false
          }
        ]
      ],
      "startDelay": 0,
      "joinOn": [],
      "optional": false,
      "defaultExclusiveJoinTask": [],
      "asyncComplete": false,
      "loopOver": [],
      "onStateChange": {},
      "permissive": false
    },
    {
      "name": "join",
      "taskReferenceName": "join_doGets",
      "inputParameters": {},
      "type": "JOIN",
      "decisionCases": {},
      "defaultCase": [],
      "forkTasks": [],
      "startDelay": 0,
      "joinOn": [
        "inline_retrieveCurrentBatteryExportSetting",
        "inline_retrieveCurrentFeedInPrice"
      ],
      "optional": false,
      "defaultExclusiveJoinTask": [],
      "asyncComplete": false,
      "loopOver": [],
      "onStateChange": {},
      "permissive": false
    },
    {
      "name": "switch",
      "taskReferenceName": "switch_updateBatteryExportSetting",
      "inputParameters": {
        "currentBatteryExportSetting": "${join_doGets.output.inline_retrieveCurrentBatteryExportSetting.result}",
        "currentFeedInPrice": "${join_doGets.output.inline_retrieveCurrentFeedInPrice.result}"
      },
      "type": "SWITCH",
      "decisionCases": {
        "never": [
          {
            "name": "http",
            "taskReferenceName": "http_setBatteryExportNever",
            "inputParameters": {
              "uri": "https://api.netzero.energy/api/v1/${workflow.secrets.netzero_site_id}/config",
              "method": "POST",
              "accept": "application/json",
              "contentType": "application/json",
              "encode": true,
              "body": {
                "energy_exports": "never"
              },
              "hedgingConfig": {
                "maxAttempts": 3
              },
              "headers": {
                "Authorization": "Bearer ${workflow.secrets.netzero_api_token}"
              }
            },
            "type": "HTTP",
            "decisionCases": {},
            "defaultCase": [],
            "forkTasks": [],
            "startDelay": 0,
            "joinOn": [],
            "optional": false,
            "defaultExclusiveJoinTask": [],
            "asyncComplete": false,
            "loopOver": [],
            "onStateChange": {},
            "permissive": false
          }
        ],
        "battery_ok": [
          {
            "name": "http",
            "taskReferenceName": "http_setBatteryExportBatteryOK",
            "inputParameters": {
              "uri": "https://api.netzero.energy/api/v1/${workflow.secrets.netzero_site_id}/config",
              "method": "POST",
              "accept": "application/json",
              "contentType": "application/json",
              "encode": true,
              "body": {
                "energy_exports": "battery_ok"
              },
              "hedgingConfig": {
                "maxAttempts": 3
              },
              "headers": {
                "Authorization": "Bearer ${workflow.secrets.netzero_api_token}"
              }
            },
            "type": "HTTP",
            "decisionCases": {},
            "defaultCase": [],
            "forkTasks": [],
            "startDelay": 0,
            "joinOn": [],
            "optional": false,
            "defaultExclusiveJoinTask": [],
            "asyncComplete": false,
            "loopOver": [],
            "onStateChange": {},
            "permissive": false
          }
        ]
      },
      "defaultCase": [],
      "forkTasks": [],
      "startDelay": 0,
      "joinOn": [],
      "optional": false,
      "defaultExclusiveJoinTask": [],
      "asyncComplete": false,
      "loopOver": [],
      "evaluatorType": "graaljs",
      "expression": "(function() {\n  // priceKwh feed in > 0 represents negative feed in to grid - exporting should be stopped\n  if ($.currentFeedInPrice > 0 && $.currentBatteryExportSetting !== \"never\")\n    return \"never\";\n  else if ($.currentFeedInPrice <= 0 && $.currentBatteryExportSetting !== \"battery_ok\")\n    return \"battery_ok\";\n  else\n    return false;\n})();\n",
      "onStateChange": {},
      "permissive": false
    }
  ],
  "inputParameters": [
    "netzero_api_token",
    "netzero_site_id",
    "amber_api_token",
    "amber_site_id"
  ],
  "outputParameters": {},
  "failureWorkflow": "",
  "schemaVersion": 2,
  "restartable": true,
  "workflowStatusListenerEnabled": false,
  "ownerEmail": "your.email@gmail.com",
  "timeoutPolicy": "ALERT_ONLY",
  "timeoutSeconds": 0,
  "variables": {},
  "inputTemplate": {},
  "rateLimitConfig": {
    "rateLimitKey": "max",
    "concurrentExecLimit": 1
  },
  "enforceSchema": true,
  "metadata": {},
  "maskedFields": []
}
  1. Select Save > Confirm.

This saves the workflow with references to the configured API tokens and site IDs. You only need to modify the workflow if you have saved the secrets using names different from those specified in the tutorial.

Step 4: Run Workflow

To test the workflow for the first time, wait for the feed-in price to fall to a negative value and then run the workflow, by selecting the Execute button.

A screenshot of a successful workflow execution

After the execution completes, open the Tesla Powerwall app and verify that the system is not exporting surplus energy to the grid.

Step 5: Automate using Workflow Scheduler

In the last step, you ran the workflow manually. To automate this process, schedule the workflow to run every five minutes using Conductor’s Workflow Scheduler.

To create a Scheduler:

  1. Go to Definitions > Scheduler from the left navigation menu on your Conductor cluster.
  2. Select + Define schedule
  3. Create a schedule for every 5 minutes using this Cron expression
bash
0 0/5 * ? * *
  1. For Workflow name, select the TeslaPowerwallSolarCurtailment workflow created earlier and Latest version
  2. For the Input params section, simply enter an empty JSON object {}
  3. Save the schedule, ensuring that the Start schedule paused? toggle is not active

A screenshot of a completed schedule

This ensures the workflow runs every five minutes and continuously evaluates whether energy export should be enabled or disabled based on current pricing.

And that’s it! Your Tesla Powerwall is now optimised to prevent exporting to the grid when feed-in prices are negative, saving you money and expediting the payback period of your equipment.

FAQs

Q. Does this workflow impact or replace Amber SmartShift?

No, this is a complimentary service that will not impact SmartShift’s ability to optimise other aspects of your system.

Q. How much does it cost to keep this workflow running?

Nothing! Orkes Developer Edition and NetZero’s Basic tier are free to use.

Q. How can I introspect previous workflow executions to see what the outcomes were?

Sure! Head to Executions > Workflow on the left hand menu in Orkes.

Any further questions? Reach out to me at jon.cochran@orkes.io