diff --git a/api/sonicapi/api.go b/api/sonicapi/api.go
index 967f8b5b0..73a604632 100644
--- a/api/sonicapi/api.go
+++ b/api/sonicapi/api.go
@@ -15,3 +15,16 @@
// along with Sonic. If not, see .
package sonicapi
+
+import "github.com/0xsoniclabs/sonic/api/ethapi"
+
+type PublicBundleAPI struct {
+ b ethapi.Backend
+}
+
+// NewPublicBundleAPI creates a new instance of the PublicBundleAPI with the given backend.
+func NewPublicBundleAPI(b ethapi.Backend) *PublicBundleAPI {
+ return &PublicBundleAPI{
+ b: b,
+ }
+}
diff --git a/api/sonicapi/bundle_api.go b/api/sonicapi/bundle_api.go
new file mode 100644
index 000000000..4770bb9e2
--- /dev/null
+++ b/api/sonicapi/bundle_api.go
@@ -0,0 +1,53 @@
+// Copyright 2026 Sonic Operations Ltd
+// This file is part of the Sonic Client
+//
+// Sonic is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// Sonic is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with Sonic. If not, see .
+
+package sonicapi
+
+import (
+ "github.com/0xsoniclabs/sonic/gossip/blockproc/bundle"
+ "github.com/ethereum/go-ethereum/common"
+ "github.com/ethereum/go-ethereum/rpc"
+)
+
+type RPCExecutionStep struct {
+ From common.Address `json:"from"`
+ Hash common.Hash `json:"hash"`
+}
+
+type RPCExecutionPlan struct {
+ Flags bundle.ExecutionFlags `json:"flags"`
+ Steps []RPCExecutionStep `json:"steps"`
+ Earliest rpc.BlockNumber `json:"earliest"`
+ Latest rpc.BlockNumber `json:"latest"`
+}
+
+// NewRPCExecutionPlan converts a bundle.ExecutionPlan to an RPCExecutionPlan for JSON-RPC responses.
+func NewRPCExecutionPlan(plan bundle.ExecutionPlan) RPCExecutionPlan {
+ steps := make([]RPCExecutionStep, len(plan.Steps))
+ for i, step := range plan.Steps {
+ steps[i] = RPCExecutionStep{
+ From: step.From,
+ Hash: step.Hash,
+ }
+ }
+
+ return RPCExecutionPlan{
+ Flags: plan.Flags,
+ Steps: steps,
+ Earliest: rpc.BlockNumber(plan.Range.Earliest),
+ Latest: rpc.BlockNumber(plan.Range.Latest),
+ }
+}
diff --git a/api/sonicapi/bundle_api_test.go b/api/sonicapi/bundle_api_test.go
new file mode 100644
index 000000000..bbeb022be
--- /dev/null
+++ b/api/sonicapi/bundle_api_test.go
@@ -0,0 +1,57 @@
+// Copyright 2026 Sonic Operations Ltd
+// This file is part of the Sonic Client
+//
+// Sonic is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// Sonic is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with Sonic. If not, see .
+
+package sonicapi
+
+import (
+ "testing"
+
+ "github.com/0xsoniclabs/sonic/gossip/blockproc/bundle"
+ "github.com/ethereum/go-ethereum/common"
+ "github.com/ethereum/go-ethereum/rpc"
+ "github.com/stretchr/testify/require"
+)
+
+func TestNewRPCExecutionPlan(t *testing.T) {
+ plan := bundle.ExecutionPlan{
+ Steps: []bundle.ExecutionStep{
+ {
+ From: common.HexToAddress("0x1111111111111111111111111111111111111111"),
+ Hash: common.HexToHash("0x2222222222222222222222222222222222222222222222222222222222222222"),
+ },
+ {
+ From: common.HexToAddress("0x3333333333333333333333333333333333333333"),
+ Hash: common.HexToHash("0x4444444444444444444444444444444444444444444444444444444444444444"),
+ },
+ },
+ Flags: bundle.EF_TolerateInvalid | bundle.EF_OneOf,
+ Range: bundle.BlockRange{
+ Earliest: 100,
+ Latest: 200,
+ },
+ }
+
+ rpcPlan := NewRPCExecutionPlan(plan)
+
+ require.Equal(t, plan.Flags, rpcPlan.Flags)
+ require.Equal(t, rpc.BlockNumber(plan.Range.Earliest), rpcPlan.Earliest)
+ require.Equal(t, rpc.BlockNumber(plan.Range.Latest), rpcPlan.Latest)
+ require.Len(t, rpcPlan.Steps, len(plan.Steps))
+ for i, step := range plan.Steps {
+ require.Equal(t, step.From, rpcPlan.Steps[i].From)
+ require.Equal(t, step.Hash, rpcPlan.Steps[i].Hash)
+ }
+}