{"openapi":"3.1.0","info":{"title":"Juicy Optimiser Partner API","version":"1.0.0","description":"REST control surface for external optimiser partners.\n\n## Authentication\n\nEvery request must present a client certificate signed by the Juicy\nPartner CA. mTLS happens at the transport layer, so the browser-based\n**Try it out** panel cannot authenticate — each operation below shows a\n`curl` example with the right `--cert` / `--key` flags. Substitute your\nown partner cert filenames.\n\n## Pagination (cursor-based)\n\nList endpoints return:\n\n```json\n{\n  \"data\":        [ ... ],\n  \"next_cursor\": \"eyJzIjogIkpVSUNZLTAwMDEyIiwgImlkIjogIi4uLiJ9\",\n  \"has_more\":    true\n}\n```\n\n- `next_cursor` is an **opaque** string. Don't parse it — just pass it\n  back unchanged as `?cursor=<value>` to fetch the next page.\n- `next_cursor` is `null` when you have reached the end. Stop iterating\n  when you see `null` (or `has_more: false`).\n- Cursors are stable under inserts: a battery added between pages\n  appears in a later page, never duplicated or skipped.\n- There is no `total` count. Counting the full assigned fleet on every\n  list call defeats the point of cursor pagination at scale; iterate\n  with cursors instead.\n- `limit` defaults to 100 and may be raised up to 500 per page.\n\nTypical iteration:\n\n```bash\ncursor=\"\"\nwhile :; do\n  resp=$(curl -s --cert partner.crt --key partner.key \\\n    \"https://partner.juicy.energy/v1/optimiser/batteries?limit=200&cursor=$cursor\")\n  echo \"$resp\" | jq '.data[]'\n  cursor=$(echo \"$resp\" | jq -r '.next_cursor // empty')\n  [ -z \"$cursor\" ] && break\ndone\n```\n\n## Filter params\n\nList endpoints accept filters that narrow the result set so you don't\nhave to walk the full fleet on every poll.\n\n- **`modified_since=<ISO-8601>`** — return only rows whose state has\n  changed since the given timestamp. Use this for incremental syncs:\n  store the time you last pulled successfully, pass it on the next\n  call, and you'll receive only what's changed. Pair with cursor\n  pagination if the delta itself is large.\n\nFuture filter params will follow the same pattern: optional, additive,\nand intended to let you avoid full-fleet scans in steady-state.\n"},"paths":{"/v1/optimiser/batteries":{"get":{"tags":["Optimiser"],"summary":"List My Batteries","description":"List batteries assigned to the calling partner.\n\nKeyset-paginated on `(serial_number, id)`. Follow `next_cursor` until\nit is `null`. `modified_since` filters to batteries whose row has\nchanged since the given ISO timestamp — partners syncing incrementally\nshould pass their last successful sync time.\n\n**Example (mTLS)**\n\n```bash\ncurl \\\n  --cert partner.crt --key partner.key \\\n  -X GET \\\n  https://partner.juicy.energy/v1/optimiser/batteries\n```","operationId":"list_my_batteries_v1_optimiser_batteries_get","parameters":[{"name":"limit","in":"query","required":false,"schema":{"type":"integer","maximum":500,"minimum":1,"default":100,"title":"Limit"}},{"name":"cursor","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Cursor"}},{"name":"modified_since","in":"query","required":false,"schema":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Modified Since"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CursorPage_PartnerBatteryResponse_"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/optimiser/batteries/{battery_id}":{"get":{"tags":["Optimiser"],"summary":"Get My Battery","operationId":"get_my_battery_v1_optimiser_batteries__battery_id__get","parameters":[{"name":"battery_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Battery Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PartnerBatteryResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"description":"**Example (mTLS)**\n\n```bash\ncurl \\\n  --cert partner.crt --key partner.key \\\n  -X GET \\\n  https://partner.juicy.energy/v1/optimiser/batteries/{battery_id}\n```"}},"/v1/optimiser/batteries/{battery_id}/telemetry":{"get":{"tags":["Optimiser"],"summary":"Get My Battery Telemetry","description":"Recent raw telemetry rows for the partner's battery.\n\n**Example (mTLS)**\n\n```bash\ncurl \\\n  --cert partner.crt --key partner.key \\\n  -X GET \\\n  https://partner.juicy.energy/v1/optimiser/batteries/{battery_id}/telemetry\n```","operationId":"get_my_battery_telemetry_v1_optimiser_batteries__battery_id__telemetry_get","parameters":[{"name":"battery_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Battery Id"}},{"name":"hours","in":"query","required":false,"schema":{"type":"integer","default":24,"title":"Hours"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response Get My Battery Telemetry V1 Optimiser Batteries  Battery Id  Telemetry Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/optimiser/batteries/{battery_id}/flex-plan":{"get":{"tags":["Optimiser"],"summary":"Get My Battery Flex Plan","operationId":"get_my_battery_flex_plan_v1_optimiser_batteries__battery_id__flex_plan_get","parameters":[{"name":"battery_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Battery Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/FlexPlanResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"description":"**Example (mTLS)**\n\n```bash\ncurl \\\n  --cert partner.crt --key partner.key \\\n  -X GET \\\n  https://partner.juicy.energy/v1/optimiser/batteries/{battery_id}/flex-plan\n```"},"post":{"tags":["Optimiser"],"summary":"Submit My Battery Flex Plan","operationId":"submit_my_battery_flex_plan_v1_optimiser_batteries__battery_id__flex_plan_post","parameters":[{"name":"battery_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Battery Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/FlexPlanSubmit"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/FlexPlanSubmitResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"description":"**Example (mTLS)**\n\n```bash\ncurl \\\n  --cert partner.crt --key partner.key \\\n  -X POST \\\n  https://partner.juicy.energy/v1/optimiser/batteries/{battery_id}/flex-plan \\\n  -H \"Content-Type: application/json\" \\\n  -d '{ ... }'\n```"},"delete":{"tags":["Optimiser"],"summary":"Clear My Battery Flex Plan","operationId":"clear_my_battery_flex_plan_v1_optimiser_batteries__battery_id__flex_plan_delete","parameters":[{"name":"battery_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Battery Id"}}],"responses":{"204":{"description":"Successful Response"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"description":"**Example (mTLS)**\n\n```bash\ncurl \\\n  --cert partner.crt --key partner.key \\\n  -X DELETE \\\n  https://partner.juicy.energy/v1/optimiser/batteries/{battery_id}/flex-plan\n```"}}},"components":{"schemas":{"CursorPage_PartnerBatteryResponse_":{"properties":{"data":{"items":{"$ref":"#/components/schemas/PartnerBatteryResponse"},"type":"array","title":"Data"},"next_cursor":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Next Cursor"},"has_more":{"type":"boolean","title":"Has More","default":false}},"type":"object","required":["data"],"title":"CursorPage[PartnerBatteryResponse]"},"FlexBlockResponse":{"properties":{"start_time_ms":{"type":"integer","title":"Start Time Ms"},"end_time_ms":{"type":"integer","title":"End Time Ms"},"strategy":{"$ref":"#/components/schemas/FlexBlockStrategy"},"parameters":{"additionalProperties":true,"type":"object","title":"Parameters"},"seq":{"type":"integer","title":"Seq"}},"type":"object","required":["start_time_ms","end_time_ms","strategy","parameters","seq"],"title":"FlexBlockResponse"},"FlexBlockStrategy":{"type":"string","enum":["self_consumption","fixed_power_setpoint","fixed_power_direction"],"title":"FlexBlockStrategy","description":"Strategies supported by the optimiser flex-block API in v1."},"FlexBlockSubmit":{"properties":{"start_time_ms":{"type":"integer","minimum":0.0,"title":"Start Time Ms"},"end_time_ms":{"type":"integer","minimum":0.0,"title":"End Time Ms"},"strategy":{"$ref":"#/components/schemas/FlexBlockStrategy"},"setpoint_w":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Setpoint W","description":"For fixed_power_setpoint: +charge, -discharge (asset convention)"},"direction":{"anyOf":[{"type":"string","enum":["charge","discharge"]},{"type":"null"}],"title":"Direction","description":"For fixed_power_direction: which direction to run at max power"},"max_power_w":{"anyOf":[{"type":"integer","minimum":0.0},{"type":"null"}],"title":"Max Power W","description":"For fixed_power_direction: ceiling (defaults to battery max)"}},"type":"object","required":["start_time_ms","end_time_ms","strategy"],"title":"FlexBlockSubmit","description":"One block in a submitted plan."},"FlexPlanResponse":{"properties":{"battery_id":{"type":"string","format":"uuid","title":"Battery Id"},"revision":{"type":"integer","title":"Revision"},"submitted_at":{"type":"string","format":"date-time","title":"Submitted At"},"horizon_end_ms":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Horizon End Ms"},"blocks":{"items":{"$ref":"#/components/schemas/FlexBlockResponse"},"type":"array","title":"Blocks"}},"type":"object","required":["battery_id","revision","submitted_at","horizon_end_ms","blocks"],"title":"FlexPlanResponse"},"FlexPlanSubmit":{"properties":{"blocks":{"items":{"$ref":"#/components/schemas/FlexBlockSubmit"},"type":"array","title":"Blocks"}},"type":"object","title":"FlexPlanSubmit","description":"Full snapshot of the plan for a battery."},"FlexPlanSubmitResponse":{"properties":{"battery_id":{"type":"string","format":"uuid","title":"Battery Id"},"revision":{"type":"integer","title":"Revision"},"accepted_block_count":{"type":"integer","title":"Accepted Block Count"},"effective_at":{"type":"string","format":"date-time","title":"Effective At"}},"type":"object","required":["battery_id","revision","accepted_block_count","effective_at"],"title":"FlexPlanSubmitResponse"},"HTTPValidationError":{"properties":{"detail":{"items":{"$ref":"#/components/schemas/ValidationError"},"type":"array","title":"Detail"}},"type":"object","title":"HTTPValidationError"},"PartnerBatteryResponse":{"properties":{"id":{"type":"string","format":"uuid","title":"Id"},"serial_number":{"type":"string","title":"Serial Number"},"display_name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Display Name"},"external_battery_ref":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"External Battery Ref"},"capacity_kwh":{"type":"number","title":"Capacity Kwh"},"max_power_kw":{"type":"number","title":"Max Power Kw"},"min_soc_percent":{"type":"number","title":"Min Soc Percent"},"max_soc_percent":{"type":"number","title":"Max Soc Percent"},"current_soc":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Current Soc"},"current_power_kw":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Current Power Kw"},"status":{"type":"string","title":"Status"},"last_seen":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Last Seen"},"timezone":{"type":"string","title":"Timezone"},"control_mode":{"type":"string","title":"Control Mode"},"optimiser_steering_active":{"type":"boolean","title":"Optimiser Steering Active"}},"type":"object","required":["id","serial_number","display_name","external_battery_ref","capacity_kwh","max_power_kw","min_soc_percent","max_soc_percent","current_soc","current_power_kw","status","last_seen","timezone","control_mode","optimiser_steering_active"],"title":"PartnerBatteryResponse","description":"Minimal battery view exposed to partners."},"ValidationError":{"properties":{"loc":{"items":{"anyOf":[{"type":"string"},{"type":"integer"}]},"type":"array","title":"Location"},"msg":{"type":"string","title":"Message"},"type":{"type":"string","title":"Error Type"}},"type":"object","required":["loc","msg","type"],"title":"ValidationError"}}},"tags":[]}